Package org.geotools.gce.imagemosaic

Source Code of org.geotools.gce.imagemosaic.ImageMosaicReader$ImageMosaicFileCollectionWalker

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

import java.awt.Rectangle;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.spi.ImageReaderSpi;
import javax.media.jai.ImageLayout;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
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.DefaultHarvestedSource;
import org.geotools.coverage.grid.io.DimensionDescriptor;
import org.geotools.coverage.grid.io.GranuleSource;
import org.geotools.coverage.grid.io.HarvestedSource;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.coverage.grid.io.StructuredGridCoverage2DReader;
import org.geotools.data.DataSourceException;
import org.geotools.data.DataUtilities;
import org.geotools.factory.Hints;
import org.geotools.gce.imagemosaic.ImageMosaicEventHandlers.ExceptionEvent;
import org.geotools.gce.imagemosaic.ImageMosaicEventHandlers.FileProcessingEvent;
import org.geotools.gce.imagemosaic.ImageMosaicEventHandlers.ProcessingEvent;
import org.geotools.gce.imagemosaic.OverviewsController.OverviewLevel;
import org.geotools.gce.imagemosaic.Utils.Prop;
import org.geotools.gce.imagemosaic.catalog.CatalogConfigurationBean;
import org.geotools.gce.imagemosaic.catalog.GranuleCatalog;
import org.geotools.gce.imagemosaic.catalog.GranuleCatalogFactory;
import org.geotools.gce.imagemosaic.catalog.MultiLevelROIProvider;
import org.geotools.gce.imagemosaic.catalog.MultiLevelROIProviderFactory;
import org.geotools.gce.imagemosaic.catalogbuilder.CatalogBuilderConfiguration;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.geotools.util.Utilities;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.geometry.BoundingBox;
import org.opengis.metadata.Identifier;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterValue;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
/**
* Reader responsible for providing access to mosaic of georeferenced
* images. Citing JAI documentation:
*
* The "Mosaic" operation creates a mosaic of two or more source images. This
* operation could be used for example to assemble a set of overlapping
* geospatially rectified images into a contiguous image. It could also be used
* to create a montage of photographs such as a panorama.
*
* All source images are assumed to have been geometrically mapped into a common
* coordinate space. The origin (minX, minY) of each image is therefore taken to
* represent the location of the respective image in the common coordinate
* system of the source images. This coordinate space will also be that of the
* destination image.
*
* All source images must have the same data type and sample size for all bands
* and have the same number of bands as color components. The destination will
* have the same data type, sample size, and number of bands and color
* components as the sources.
*
*
* @author Simone Giannecchini, GeoSolutions S.A.S
* @author Stefan Alfons Krueger (alfonx), Wikisquare.de : Support for jar:file:foo.jar/bar.properties URLs
* @since 2.3
*
*
*
* @source $URL$
*/
@SuppressWarnings("rawtypes")
public class ImageMosaicReader extends AbstractGridCoverage2DReader implements StructuredGridCoverage2DReader {


    Set<String> names = new HashSet<String>();

    String defaultName = null;

    public static final String UNSPECIFIED = "_UN$PECIFIED_";

    Map<String, RasterManager> rasterManagers = new ConcurrentHashMap<String, RasterManager>();

    public RasterManager getRasterManager(String name) {
          if(name != null && rasterManagers.containsKey(name)){
              return rasterManagers.get(name);
          }
          return null;
  }
   
    @Override
    public String[] getGridCoverageNames() {
        return (String[]) names.toArray(new String[]{});
    }

    /** Logger. */
  private final static Logger LOGGER = org.geotools.util.logging.Logging.getLogger(ImageMosaicReader.class);

  /**
   * The source {@link URL} pointing to the index shapefile for this
   * {@link ImageMosaicReader}.
   */
  URL sourceURL;
 
        File parentDirectory;

  boolean expandMe;
 
  PathType pathType;
 
  ExecutorService multiThreadedLoader;

  String locationAttributeName=Utils.DEFAULT_LOCATION_ATTRIBUTE;

        int maxAllowedTiles=ImageMosaicFormat.MAX_ALLOWED_TILES.getDefaultValue();

  /** The suggested SPI to avoid SPI lookup*/
  ImageReaderSpi suggestedSPI;
 
  GranuleCatalog granuleCatalog;

  boolean cachingIndex;

  boolean imposedBBox;
 
  boolean heterogeneousGranules;

        boolean checkAuxiliaryMetadata = false;

  String typeName;

  /**
   * Enumeration object used for defining 3 different behaviours for the Harvesting, each of them associated
   * to one of these 3 objects:
   * <ul>
   * <li>File</li>
   * <li>Directory</li>
   * <li>List of Files</li>
   * </ul>
   *
   * @author Nicola Lagomarsini, GeoSolutions S.A.S.
   *
   */
  public enum HarvestedResource{
      FILE {
            @Override
            public void harvest(String defaultCoverage, Object source, Hints hints, final List<HarvestedSource> result, ImageMosaicReader reader) {
                File file;
                if (source instanceof Collection<?>) {
                    file = (File) ((Collection<?>) source).iterator().next();
                } else {
                    file = (File) source;
                }
                // Directory associated to the input File
                File directory = file.getParentFile();
                // File Filter associated to the input File
                IOFileFilter filter = FileFilterUtils.nameFileFilter(file.getName());
                // Harvesting file
                harvestCalculation(defaultCoverage, result, reader, directory, filter);
               
            }
        }, DIRECTORY {
            @Override
            public void harvest(String defaultCoverage, Object source, Hints hints, final List<HarvestedSource> result, ImageMosaicReader reader) {
                File directory;
                if (source instanceof Collection<?>) {
                    directory = (File) ((Collection<?>) source).iterator().next();
                } else {
                    directory = (File) source;
                }
                // Harvesting directory
                harvestCalculation(defaultCoverage, result, reader, directory, null);
               
            }
        }, FILE_COLLECTION {
            @Override
            public void harvest(String defaultCoverage, Object source, Hints hints, final List<HarvestedSource> result, ImageMosaicReader reader) {
                // I have already checked that it is a Collection of File objects
                Collection<File> files = (Collection<File>) source;
               
                // Harvesting
            
                // prepare the walker configuration
                CatalogBuilderConfiguration configuration = new CatalogBuilderConfiguration();
                configuration.setParameter(Prop.ABSOLUTE_PATH, Boolean.toString(true));
               
                // Setting of the HARVEST_DIRECTORY property for passing the checks even if it is not used
                // Selection of the first file
                Iterator<File> it = files.iterator();
                String indexingPath = it.next().getAbsolutePath();
                configuration.setParameter(Prop.HARVEST_DIRECTORY, indexingPath);
               
                if(defaultCoverage == null) {
                    String[] coverageNames = reader.getGridCoverageNames();
                    defaultCoverage = (coverageNames != null && coverageNames.length > 0) ? coverageNames[0] : Utils.DEFAULT_INDEX_NAME;
                }
               
                configuration.setParameter(Prop.INDEX_NAME, defaultCoverage);
                configuration.setHints(new Hints(Utils.MOSAIC_READER, reader));
               
                File mosaicSource = DataUtilities.urlToFile(reader.sourceURL);
                if(!mosaicSource.isDirectory()) {
                    mosaicSource = mosaicSource.getParentFile();
                }
               
                configuration.setParameter(Prop.ROOT_MOSAIC_DIR, mosaicSource.getAbsolutePath());
               
                // run the walker and collect information
                ImageMosaicEventHandlers eventHandler = new ImageMosaicEventHandlers();
                final ImageMosaicConfigHandler catalogHandler = new ImageMosaicConfigHandler(configuration,
                        eventHandler);
                // Creation of the Walker for the File List
                ImageMosaicFileCollectionWalker walker = new ImageMosaicFileCollectionWalker(catalogHandler, eventHandler,files);
                eventHandler.addProcessingEventListener(new ImageMosaicEventHandlers.ProcessingEventListener() {
                   
                    @Override
                    public void getNotification(ProcessingEvent event) {
                        if(event instanceof FileProcessingEvent) {
                            FileProcessingEvent fileEvent = (FileProcessingEvent) event;
                            result.add(new DefaultHarvestedSource(fileEvent.getFile(), fileEvent.isIngested(), fileEvent.getMessage()));
                        }
                    }
                   
                    @Override
                    public void exceptionOccurred(ExceptionEvent event) {
                        // nothing to do
                    }
                });
                // Wait the Walker ends its operations
                walker.run();
            }
        };
     
      HarvestedResource(){}
     
      /**
       * Harvesting of the input resource. The result will be strored inside the {@link List} object.
       *
       * @param defaultCoverage
       * @param source
       * @param hints
       * @param result
       * @param reader
       */
      public abstract void harvest(String defaultCoverage, Object source, Hints hints, final List<HarvestedSource> result, ImageMosaicReader reader);
     
      /**
       * Returns the HarvestedResource associated to the input Object
       *
       * @param source
       * @return
       */
            public static HarvestedResource getResourceFromObject(Object source) {
                // Check if the resource is a File or a Directory
                if (source instanceof File) {
                    return getResourceFromFile((File) source);
                }
                // For a String instance, it is converted to String
                if (source instanceof String) {
                    File file = new File((String) source);
                    return getResourceFromFile(file);
                }
                // Check if the input Object is a File Collection
                if (source instanceof Collection<?>) {
                    Collection<File> files = null;
                    try {
                        files = (Collection<File>) source;
                    } catch (ClassCastException e) {
                        // Log the exception
                        if (LOGGER.isLoggable(Level.WARNING)) {
                            LOGGER.log(Level.WARNING, e.getMessage(), e);
                        }
                    }
                    // If the files are present
                    if (files != null) {
                        // No File is saved
                        int fileSize = files.size();
                        // Check on the File Size
                        if (fileSize < 1) {
                            return null;
                        } else if (fileSize == 1) {
                            // If the Collection size is 1 then the object can be only a file or a directory
                            return getResourceFromFile(files.iterator().next());
                        } else {
                            return FILE_COLLECTION;
                        }
   
                    }
                }
                return null;
            }

      /**
       * Check if the File Object is a DIRECTORY or not and return the associated {@link HarvestedResource}
       *
       * @param file
       * @return
       */
            private static HarvestedResource getResourceFromFile(File file) {
                if(file != null){
                    if(file.isDirectory()){
                        return DIRECTORY;
                    }else{
                        return FILE;
                    }                   
                }
                return null;
            }
           
            /**
             * Method for harvesting on a directory
             *
             * @param defaultCoverage
             * @param result
             * @param reader
             * @param directory
             * @param filter
             */
            private static void harvestCalculation(String defaultCoverage,
                    final List<HarvestedSource> result, ImageMosaicReader reader, File directory, IOFileFilter filter) {
                // prepare the walker configuration
                CatalogBuilderConfiguration configuration = new CatalogBuilderConfiguration();
                configuration.setParameter(Prop.ABSOLUTE_PATH, Boolean.toString(Utils.DEFAULT_PATH_BEHAVIOR));
                String indexingPath = directory.getAbsolutePath();
                configuration.setParameter(Prop.HARVEST_DIRECTORY, indexingPath);
                if(defaultCoverage == null) {
                    String[] coverageNames = reader.getGridCoverageNames();
                    defaultCoverage = (coverageNames != null && coverageNames.length > 0) ? coverageNames[0] : Utils.DEFAULT_INDEX_NAME;
                }
                configuration.setParameter(Prop.INDEX_NAME, defaultCoverage);
                configuration.setHints(new Hints(Utils.MOSAIC_READER, reader));
               
                File mosaicSource = DataUtilities.urlToFile(reader.sourceURL);
                if(!mosaicSource.isDirectory()) {
                    mosaicSource = mosaicSource.getParentFile();
                }
               
                configuration.setParameter(Prop.ROOT_MOSAIC_DIR, mosaicSource.getAbsolutePath());
               
                // run the walker and collect information
                ImageMosaicEventHandlers eventHandler = new ImageMosaicEventHandlers();
                final ImageMosaicConfigHandler catalogHandler = new ImageMosaicConfigHandler(configuration,
                        eventHandler);
                // build the index
                ImageMosaicDirectoryWalker walker = new ImageMosaicDirectoryWalker(catalogHandler, eventHandler,filter);
                eventHandler.addProcessingEventListener(new ImageMosaicEventHandlers.ProcessingEventListener() {
                   
                    @Override
                    public void getNotification(ProcessingEvent event) {
                        if(event instanceof FileProcessingEvent) {
                            FileProcessingEvent fileEvent = (FileProcessingEvent) event;
                            result.add(new DefaultHarvestedSource(fileEvent.getFile(), fileEvent.isIngested(), fileEvent.getMessage()));
                        }
                    }
                   
                    @Override
                    public void exceptionOccurred(ExceptionEvent event) {
                        // nothing to do
                    }
                });

                walker.run();
            }
  }
 
    /**
     * Constructor.
     *
     * @param source The source object.
     * @throws IOException
     * @throws UnsupportedEncodingException
     *
     */
    public ImageMosaicReader(Object source, Hints uHints) throws IOException {
        super(source, uHints);

        //
        // try to extract a multithreaded loader if available
        //
        if (this.hints.containsKey(Hints.EXECUTOR_SERVICE)) {
            final Object executor = uHints.get(Hints.EXECUTOR_SERVICE);
            if (executor != null && executor instanceof ExecutorService) {
                multiThreadedLoader = (ExecutorService) executor;
                if (LOGGER.isLoggable(Level.FINE)) {
                    if (multiThreadedLoader instanceof ThreadPoolExecutor) {
                        final ThreadPoolExecutor tpe = (ThreadPoolExecutor) multiThreadedLoader;
                        LOGGER.fine("Using ThreadPoolExecutor with the following settings: "
                                + "core pool size = " + tpe.getCorePoolSize()
                                + "\nmax pool size = " + tpe.getMaximumPoolSize()
                                + "\nkeep alive time "
                                + tpe.getKeepAliveTime(TimeUnit.MILLISECONDS));
                    }
                }
            }
        }

        // max allowed tiles for a single request
        if (this.hints.containsKey(Hints.MAX_ALLOWED_TILES))
            this.maxAllowedTiles = ((Integer) this.hints.get(Hints.MAX_ALLOWED_TILES));

        //
        // Check source
        //
        if (source instanceof ImageMosaicDescriptor) {
            initReaderFromDescriptor((ImageMosaicDescriptor) source, uHints);
        } else {
            try {
               
             // Cloning the hints
                Hints localHints = new Hints(uHints);
                if (localHints != null) {
                    localHints.add(new Hints(Utils.MOSAIC_READER, this));
                }
                initReaderFromURL(source, localHints);
            } catch (Exception e) {
                throw new DataSourceException(e);
            }
        }
    }

    /**
     * Init this {@link ImageMosaicReader} using the provided {@link ImageMosaicDescriptor} as source.
     *
     * @param source
     * @param uHints
     * @throws DataSourceException
     */
    private void initReaderFromDescriptor(final ImageMosaicDescriptor source, final Hints uHints) throws IOException {
        Utilities.ensureNonNull("source", source);
        final MosaicConfigurationBean configuration = source.getConfiguration();
        if (configuration == null) {
            throw new DataSourceException("Unable to create reader for this mosaic since we could not parse the configuration.");
        }
        extractProperties(configuration);
        GranuleCatalog catalog = source.getCatalog();
        if (catalog == null) {
            throw new DataSourceException("Unable to create reader for this mosaic since the inner catalog is null.");
        }

        final SimpleFeatureType schema = catalog.getType(configuration.getCatalogConfigurationBean().getTypeName());
        if (schema == null) {
            throw new DataSourceException("Unable to create reader for this mosaic since the inner catalog schema is null.");
        }
       
        granuleCatalog = catalog;

        // grid geometry
        setGridGeometry(typeName);

        // raster manager
        addRasterManager(configuration, true);
    }

    /**
     * Init this {@link ImageMosaicReader} using the provided object as a source referring to an {@link URL}.
     *
     * @param source
     * @param uHints
     * @throws DataSourceException
     */
    private void initReaderFromURL(final Object source, final Hints hints) throws Exception {
        this.sourceURL = Utils.checkSource(source, hints);
       
        // Preliminar check on source
        if (this.sourceURL == null) {
            throw new DataSourceException(
                    "This plugin accepts File, URL or String. The string may describe a File or an URL");
        }

        // Load properties file
        MosaicConfigurationBean configuration = null;
        try {
            if (sourceURL.getProtocol().equals("file")) {
                final File sourceFile = DataUtilities.urlToFile(sourceURL);
                if (!sourceFile.exists()) {
                    throw new DataSourceException("The specified sourceURL doesn't refer to an existing file");
                }
            }
            if (sourceURL != null) {
                parentDirectory = DataUtilities.urlToFile(sourceURL);
                if (!parentDirectory.isDirectory()) {
                        parentDirectory = parentDirectory.getParentFile();
                }
            }
            configuration = Utils.loadMosaicProperties(sourceURL, this.locationAttributeName);
            if (configuration == null) {
                //
                // do we have a datastore properties file? It will preempt on the shapefile
                //
                final File parent = DataUtilities.urlToFile(sourceURL).getParentFile();

                // this can be used to look for properties files that do NOT define a datastore
                final File[] properties = parent.listFiles((FilenameFilter) FileFilterUtils.and(
                        FileFilterUtils.notFileFilter(FileFilterUtils
                                .nameFileFilter("indexer.properties")), FileFilterUtils.and(
                                FileFilterUtils.notFileFilter(FileFilterUtils
                                        .nameFileFilter("datastore.properties")), FileFilterUtils
                                        .makeFileOnly(FileFilterUtils
                                                .suffixFileFilter(".properties")))));

                // do we have a valid datastore + mosaic properties pair?
                final File datastoreProperties = new File(parent, "datastore.properties");

                // Scan for MosaicConfigurationBeans from properties files
                List<MosaicConfigurationBean> beans = new ArrayList<MosaicConfigurationBean>();
                for (File propFile : properties) {
                    if (Utils.checkFileReadable(propFile) && Utils.loadMosaicProperties(DataUtilities.fileToURL(propFile), "") != null) {
                        configuration = Utils.loadMosaicProperties(DataUtilities.fileToURL(propFile), this.locationAttributeName);
                        if (configuration != null) {
                            beans.add(configuration);
                        }
                    }
                }
               
                // In case we didn't find any configuration bean and datastore properties, we can't do anything
                if (beans.isEmpty() && !datastoreProperties.exists()) {
                    throw new DataSourceException("No mosaic properties file or datastore properties file have been found");
                }
               
                // Catalog initialization from datastore
                GranuleCatalog catalog = null;
                final Properties params = CatalogManager.createGranuleCatalogProperties(datastoreProperties);

                // Since we are dealing with a catalog from an existing store, make sure to scan for all the typeNames on initialization
                final Object typeNames=params.get(Utils.SCAN_FOR_TYPENAMES);
                if (typeNames!=null){
                    params.put(Utils.SCAN_FOR_TYPENAMES, Boolean.valueOf(typeNames.toString()));
                } else {
                    params.put(Utils.SCAN_FOR_TYPENAMES, Boolean.TRUE);
                }
                if (beans.size() > 0) {
                    catalog = GranuleCatalogFactory.createGranuleCatalog(sourceURL, beans.get(0).getCatalogConfigurationBean(), params, getHints());
                } else {
                    catalog = CatalogManager.createGranuleCatalogFromDatastore(parent, datastoreProperties, true, getHints());
                }
                MultiLevelROIProvider rois = MultiLevelROIProviderFactory.createFootprintProvider(parent);
                catalog.setMultiScaleROIProvider(rois);
                if (granuleCatalog != null) {
                    granuleCatalog.dispose();
                }
                granuleCatalog = catalog;

                if (granuleCatalog == null) {
                    throw new DataSourceException("Unable to create index for this URL " + sourceURL);
                }

                // Creating a RasterManager for each mosaic configuration found on disk
                for (MosaicConfigurationBean bean : beans) {
                    // Add a RasterManager on top of this Mosaic configuration bean and initialize it
                    addRasterManager(bean, true);
                }
            } else {
               
                // Old style code: we have a single MosaicConfigurationBean. Use that to create the catalog
                granuleCatalog = CatalogManager.createCatalog(sourceURL, configuration, this.hints);
                File parent = DataUtilities.urlToFile(sourceURL).getParentFile();
                MultiLevelROIProvider rois = MultiLevelROIProviderFactory.createFootprintProvider(parent);
                granuleCatalog.setMultiScaleROIProvider(rois);
                addRasterManager(configuration, true);
            }

        } catch (Throwable e) {
           
            // Dispose catalog
            try {
                if (granuleCatalog != null) {
                    granuleCatalog.dispose();
                }
            } catch (Throwable e1) {
                if (LOGGER.isLoggable(Level.FINEST)) {
                    LOGGER.log(Level.FINEST, e1.getLocalizedMessage(), e1);
                }
            } finally {
                granuleCatalog = null;
            }
           
            // dispose raster managers as well
            try {
                disposeManagers();
            } catch (Throwable e1) {
                if (LOGGER.isLoggable(Level.FINEST)) {
                    LOGGER.log(Level.FINEST, e1.getLocalizedMessage(), e1);
                }
            } finally {
                rasterManagers = null;
            }

            // rethrow
            throw new DataSourceException(e);
        }
    }

    private void setGridGeometry(final ReferencedEnvelope envelope, final GranuleCatalog catalog,
            String typeName) {
        Utilities.ensureNonNull("index", catalog);
        //
        // save the bbox and prepare other info
        //
        final BoundingBox bounds = catalog.getBounds(typeName);
        if(bounds.isEmpty()) {
                throw new IllegalArgumentException("Cannot create a mosaic out of an empty index");
        }
       
        // we might have an imposed bbox
        this.crs=bounds.getCoordinateReferenceSystem();
        if(envelope==null)
          this.originalEnvelope=new GeneralEnvelope(bounds);
        else{
          this.originalEnvelope=new GeneralEnvelope(envelope);
          this.originalEnvelope.setCoordinateReferenceSystem(crs);
        }
       
        // original gridrange (estimated). I am using the floor here in order to make sure
        // we always stays inside the real area that we have for the granule
        originalGridRange = new GridEnvelope2D(
                        new Rectangle(
                                        (int) (originalEnvelope.getSpan(0)/ highestRes[0]),
                                        (int) (originalEnvelope.getSpan(1)/ highestRes[1])
                                        )
                        );
        raster2Model= new AffineTransform2D(
                highestRes[0],
                0,
                0,
                -highestRes[1],
                originalEnvelope.getLowerCorner().getOrdinate(0)+0.5*highestRes[0],
                originalEnvelope.getUpperCorner().getOrdinate(1)-0.5*highestRes[1]);  
   
  }

  private void setGridGeometry (final String typeName) {
      setGridGeometry(null, granuleCatalog, typeName);
    }
  private void extractProperties(final MosaicConfigurationBean configuration) throws IOException {

    // resolutions levels
    numOverviews = configuration.getLevelsNum() - 1;
    final double[][] resolutions = configuration.getLevels();
    overViewResolutions = numOverviews >= 1 ? new double[numOverviews][2]: null;
    highestRes = new double[2];
    highestRes[0] = resolutions[0][0];
    highestRes[1] = resolutions[0][1];

    if (LOGGER.isLoggable(Level.FINE))
      LOGGER.fine(new StringBuilder("Highest res ").append(highestRes[0])
          .append(" ").append(highestRes[1]).toString());

    if (numOverviews > 0){
         for (int i = 0; i < numOverviews; i++) {          
        overViewResolutions[i][0] = resolutions[i+1][0];
        overViewResolutions[i][1] = resolutions[i+1][1];
        
    }
 
    // name
    coverageName = configuration.getName();

    // need a color expansion?
    // this is a newly added property we have to be ready to the case where
    // we do not find it.
    expandMe = configuration.isExpandToRGB();
   
    checkAuxiliaryMetadata = configuration.isCheckAuxiliaryMetadata();
   
    CatalogConfigurationBean catalogConfigurationBean = configuration.getCatalogConfigurationBean();
   
    // do we have heterogenous granules
    heterogeneousGranules = catalogConfigurationBean.isHeterogeneous();

    // absolute or relative path
    pathType = catalogConfigurationBean.isAbsolutePath()?PathType.ABSOLUTE:PathType.RELATIVE;
   
    //
    // location attribute
    //
    locationAttributeName = catalogConfigurationBean.getLocationAttribute();
   
    // suggested SPI
    final String suggestedSPIClass = catalogConfigurationBean.getSuggestedSPI();
    if (suggestedSPIClass != null){
      try {
        final Class<?> clazz=Class.forName(suggestedSPIClass);
        if(clazz.newInstance() instanceof ImageReaderSpi)
          suggestedSPI=(ImageReaderSpi)clazz.newInstance();
        else
          suggestedSPI=null;
      } catch (Exception e) {
        if(LOGGER.isLoggable(Level.FINE))
          LOGGER.log(Level.FINE,e.getLocalizedMessage(),e);
        suggestedSPI=null;
      }
    }

    // caching for the index
    cachingIndex = catalogConfigurationBean.isCaching();
   
    // imposed BBOX
                if(configuration.getEnvelope()!=null){
                this.imposedBBox=true;
                // we set the BBOX later to retain also the CRS
                } else {
                  this.imposedBBox=false;
                }
   
    // typeName to be used for reading the mosaic
    this.typeName = catalogConfigurationBean.getTypeName();

  }

  /**
   * Constructor.
   *
   * @param source
   *            The source object.
   * @throws IOException
   * @throws UnsupportedEncodingException
   *
   */
  public ImageMosaicReader(Object source) throws IOException {
    this(source, null);

  }

  /**
   *
   * @see org.opengis.coverage.grid.GridCoverageReader#getFormat()
   */
  public Format getFormat() {
    return new ImageMosaicFormat();
  }

    public GridCoverage2D read(GeneralParameterValue[] params) throws IOException {
       return read (UNSPECIFIED, params);
  }

  /**
   *
   * @see org.opengis.coverage.grid.GridCoverageReader#read(org.opengis.parameter.GeneralParameterValue[])
   * @Override
   */
  public GridCoverage2D read(String coverageName, GeneralParameterValue[] params) throws IOException {
    
      // check if we were disposed already
      if(rasterManagers == null){
          throw new IOException("Looks like this reader has been already disposed or it has not been properly initialized.");
      }
    if (LOGGER.isLoggable(Level.FINE)) {
            if (sourceURL != null) {
          LOGGER.fine("Reading mosaic from " + sourceURL.toString());
            } else {
                LOGGER.fine("Reading mosaic");
            }
            final double[][] levels = getResolutionLevels(coverageName);
            if (levels != null) {
                final double[] highRes = levels[0];
                LOGGER.fine("Highest res "+ highRes[0] + " " + highRes[1]);
            }
       
    }
    //
    // add max allowed tiles if missing
    //
    if(this.maxAllowedTiles!=ImageMosaicFormat.MAX_ALLOWED_TILES.getDefaultValue()){
      if(params!=null){
        // first thing let's see if we have it already, in which case we do nothing since a read parameter override a Hint
        boolean found=false;
        for(GeneralParameterValue pv:params)
        {
          if(pv.getDescriptor().getName().equals(ImageMosaicFormat.MAX_ALLOWED_TILES.getName()))
          {
            found=true;
            break;
          }
        }
       
        //ok, we did not find it, let's add it back
        if(!found)
        {
          final GeneralParameterValue[] temp = new GeneralParameterValue[params.length+1];
          System.arraycopy(params, 0, temp, 0, params.length);
          ParameterValue<Integer> tempVal = ImageMosaicFormat.MAX_ALLOWED_TILES.createValue();
          tempVal.setValue(this.maxAllowedTiles);
          temp[params.length]=tempVal;
        }
      }
      else
      {
        // we do not have nay read params, we have to create the array for them
        ParameterValue<Integer> tempVal = ImageMosaicFormat.MAX_ALLOWED_TILES.createValue();
        tempVal.setValue(this.maxAllowedTiles);
        params= new GeneralParameterValue[]{tempVal};
      }
       
    }
   
    //
    // Loading tiles trying to optimize as much as possible
    //
    final Collection<GridCoverage2D> response = read(params, coverageName);
    if (response.isEmpty()) {
        if (LOGGER.isLoggable(Level.FINE)){
            LOGGER.fine("The response is empty. ==> returning a null GridCoverage");
        }
        return null;
    } else {
      return response.iterator().next();
    }
  }
 
    /**
     * Look for the parameter containing the coverage name and check its validity. Then delegate the proper RasterManager to do the read operation.
     *
     * @param params
     * @return
     * @throws IOException
     */
    private Collection<GridCoverage2D> read(GeneralParameterValue[] params, String coverageName)
            throws IOException {
        coverageName = checkUnspecifiedCoverage(coverageName);
        return getRasterManager(coverageName).read(params);
    }

    /**
     * Package private accessor for {@link Hints}.
     *
     * @return this {@link Hints} used by this reader.
     */
    Hints getHints() {
        return super.hints;
    }

    /**
     * Package private accessor for the highest resolution values.
     *
     * @return the highest resolution values.
     */
    double[] getHighestRes() {
        return super.highestRes;
    }
 
    /**
     *
     * @return
     */
    double[][] getOverviewsResolution() {
        return super.overViewResolutions;
    }

    int getNumberOfOvervies() {
        return super.numOverviews;
    }

    /** Package scope grid to world transformation accessor */
    MathTransform getRaster2Model() {
        return raster2Model;
    }
   
    /**
     * Let us retrieve the {@link GridCoverageFactory} that we want to use.
     *
     * @return
     *       retrieves the {@link GridCoverageFactory} that we want to use.
     */
    GridCoverageFactory getGridCoverageFactory(){
      return coverageFactory;
    }

    /**
     * Number of coverages for this reader is 1
     *
     * @return the number of coverages for this reader.
     */
    @Override
    public int getGridCoverageCount() {
        return names.size();
    }

    /**
     * Releases resources held by this reader.
     *
     */
    @Override
    public synchronized void dispose() {
        super.dispose();
        synchronized (this) {
            try {
                if (granuleCatalog != null)
                    this.granuleCatalog.dispose();
                disposeManagers();
            } catch (Exception e) {
                if (LOGGER.isLoggable(Level.FINE))
                    LOGGER.log(Level.FINE, e.getLocalizedMessage(), e);
            }
        }
    }

    /**
     * Dispose raster managers
     */
    private void disposeManagers() {
        if (rasterManagers != null) {
            Set<String> keys = rasterManagers.keySet();
            for (String key: keys) {
                rasterManagers.get(key).dispose();
            }
            rasterManagers.clear();
            rasterManagers = null;
        }
    }

    @Override
    public String[] getMetadataNames() {
        return getMetadataNames(UNSPECIFIED);
    }

    /**
     * Populate the metadata names array for the specified coverageName
     * @param coverageName
     * @return
     */
    @Override
    public String[] getMetadataNames(String coverageName) {
        String name = checkUnspecifiedCoverage(coverageName);
        RasterManager manager = getRasterManager(name);
        return manager != null ? manager.getMetadataNames() : null;
    }

    @Override
    public String getMetadataValue(final String name) {
        return getMetadataValue(UNSPECIFIED, name);
    }

    @Override
    public String getMetadataValue(String coverageName, final String name) {
        coverageName = checkUnspecifiedCoverage(coverageName);
        RasterManager manager = getRasterManager(coverageName);
        return manager.getMetadataValue(name);
    }

    @Override
    public Set<ParameterDescriptor<List>> getDynamicParameters() {
        return getDynamicParameters(UNSPECIFIED);
    }
   
    @Override
    public Set<ParameterDescriptor<List>> getDynamicParameters(String coverageName) {
        coverageName = checkUnspecifiedCoverage(coverageName);
        RasterManager manager =  getRasterManager(coverageName);
        return (Set<ParameterDescriptor<List>>) (manager.domainsManager != null ? manager.domainsManager
                .getDynamicParameters() :  Collections.emptySet());
    }

    public boolean isParameterSupported(Identifier name) {
        return isParameterSupported(UNSPECIFIED, name);
    }
   
    @Override
    public int getNumOverviews(String coverageName) {
        coverageName = checkUnspecifiedCoverage(coverageName);
        RasterManager manager =  getRasterManager(coverageName);
        return manager.overviewsController.getNumberOfOverviews();
    }

    @Override
    public int getNumOverviews() {
        return getNumOverviews(UNSPECIFIED);
    }

    @Override
    public double[] getReadingResolutions(OverviewPolicy policy,
            double[] requestedResolution) {
        return getReadingResolutions(UNSPECIFIED, policy, requestedResolution);
    }

    @Override
    public double[] getReadingResolutions(String coverageName, OverviewPolicy policy,
            double[] requestedResolution) {
        coverageName = checkUnspecifiedCoverage(coverageName);
        RasterManager manager =  getRasterManager(coverageName);
        final int numOverviews = getNumOverviews(coverageName);
        OverviewsController overviewsController = manager.overviewsController;
        OverviewLevel level = null;
        if (numOverviews > 0) {
            int imageIdx = overviewsController.pickOverviewLevel(policy, requestedResolution);
            level = overviewsController.getLevel(imageIdx);
        } else {
            level = overviewsController.getLevel(0);
        }
        return new double[]{level.resolutionX, level.resolutionY};
    }

        /**
         * Check whether the specified parameter is supported for the specified coverage.
         * @param coverageName
         * @param parameterName
         * @return
         */
        public boolean isParameterSupported(String coverageName, Identifier parameterName) {
            coverageName = checkUnspecifiedCoverage(coverageName);
            RasterManager manager = getRasterManager(coverageName);
            return manager.domainsManager != null ? manager.domainsManager.isParameterSupported(parameterName) : false;
    }

    /**
     * Checker whether the specified coverageName is supported. In case the name is Unspecified and the manager only has 1 coverage, then it returns
     * the only available coverage name (using default to speed up the response without need to access the set through an iterator). In case of
     * multiple coverages, throws an Exceptions if the coverage name is unspecified.
     *
     * @param coverageName
     */
    private String checkUnspecifiedCoverage(String coverageName) {
        if (coverageName.equalsIgnoreCase(UNSPECIFIED)) {
            if (getGridCoverageCount() > 1) {
                throw new IllegalArgumentException(
                        "Need to specify the coverageName for a reader related to multiple coverages");
            } else {
                return defaultName;
            }
        } else {
            if (!names.contains(coverageName)) {
                throw new IllegalArgumentException("The specified coverageName is unavailable");
            } else {
                return coverageName;
            }
        }
    }

    /**
     * Create a RasterManager on top of this {@link MosaicConfigurationBean}
     * @param configuration the {@link MosaicConfigurationBean} to be used to create the {@link RasterManager}
     * @param init {@code true} if the Manager should be initialized.
     * @return
     * @throws IOException
     */
    protected RasterManager addRasterManager(final MosaicConfigurationBean configuration, final boolean init) throws IOException {
        Utilities.ensureNonNull("MosaicConfigurationBean", configuration);
        String name = configuration.getName();
        RasterManager rasterManager = new RasterManager(this, configuration);
        rasterManagers.put(name, rasterManager);
        names.add(name);
        if (defaultName == null) {
            defaultName = name;
        }
        if (init) {
            rasterManager.initialize(false);
        }
        return rasterManager;
    }

    @Override
    public GranuleSource getGranules(final String coverageName, final boolean readOnly) throws IOException,
            UnsupportedOperationException {
        RasterManager manager = getRasterManager(coverageName);
        if (manager == null) {
            // Consider creating a new GranuleStore
        } else {
            return manager.getGranuleSource(readOnly, getHints());
        }
        return null;
    }

    @Override
    public boolean isReadOnly() {
        return false;
    }

    @Override
    public void createCoverage(String coverageName, SimpleFeatureType indexSchema) throws IOException, UnsupportedOperationException {
        RasterManager manager = getRasterManager(coverageName);
        if (manager != null) {
            manager.createStore(indexSchema);
        } else {
            throw new IOException("This implementation requires to create a RasterManager for a coverage before creating the store. " + coverageName);
        }
    }

    @Override
    public boolean removeCoverage(String coverageName, final boolean delete) throws IOException {
        return removeCoverage(coverageName, delete, true);
    }

    /**
     *
     * @param coverageName
     * @param forceDelete
     * @param checkForReferences
     *          {@code true} true in case, when deleting, we need to check whether the file is being referred by some
     *          other coverage or not. In the latter case, we can safely delete it
     *         
     * @return
     * @throws IOException
     */
    private boolean removeCoverage(String coverageName, final boolean forceDelete, final boolean checkForReferences) throws IOException {
        RasterManager manager = getRasterManager(coverageName);
        if (manager != null) {
           manager.removeStore(coverageName, forceDelete, checkForReferences);
          
           // Should I preserve managers for future re-harvesting or it's ok
           // to remove them
           rasterManagers.remove(coverageName);
           names.remove(coverageName);
           if (defaultName == coverageName) {
               Iterator<String> iterator = names.iterator();
               if (iterator.hasNext()) {
                   defaultName = iterator.next();
               } else {
                   defaultName = null;
               }
           }

           return true;
        } else {
            throw new IOException("No Raster manager have been found for the specified coverageName. " + coverageName);
        }
    }
   
   

    @Override
    public GeneralEnvelope getOriginalEnvelope() {
        return getOriginalEnvelope(UNSPECIFIED);
    }

    @Override
    public GeneralEnvelope getOriginalEnvelope(String coverageName) {
        String name = checkUnspecifiedCoverage(coverageName);
        RasterManager manager = getRasterManager(name);
        return manager.spatialDomainManager.coverageEnvelope;
    }

    @Override
    public GridEnvelope getOriginalGridRange() {
        return getOriginalGridRange(UNSPECIFIED);
    }
   
    @Override
    public GridEnvelope getOriginalGridRange(String coverageName) {
        String name = checkUnspecifiedCoverage(coverageName);
        RasterManager manager = getRasterManager(name);
        return manager.spatialDomainManager.gridEnvelope;
    }

    @Override
    public MathTransform getOriginalGridToWorld(PixelInCell pixInCell) {
        return getOriginalGridToWorld(UNSPECIFIED, pixInCell);
    }
   
    @Override
    public MathTransform getOriginalGridToWorld(String coverageName, PixelInCell pixInCell) {
        String name = checkUnspecifiedCoverage(coverageName);
        RasterManager manager = getRasterManager(name);
        return manager.spatialDomainManager.getOriginalGridToWorld(pixInCell);
    }

    @Override
    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
        return getCoordinateReferenceSystem(UNSPECIFIED);
    }
    @Override
    public CoordinateReferenceSystem getCoordinateReferenceSystem(String coverageName) {
        String name = checkUnspecifiedCoverage(coverageName);
        RasterManager manager = getRasterManager(name);
        return manager.spatialDomainManager.coverageCRS;
    }

    @Override
    public ImageLayout getImageLayout() throws IOException {
        return getImageLayout(UNSPECIFIED);
    }

    @Override
    public ImageLayout getImageLayout(String coverageName) throws IOException {
        String name = checkUnspecifiedCoverage(coverageName);
        RasterManager manager = getRasterManager(name);
        return manager.defaultImageLayout;
    }
   
    @Override
    public double[][] getResolutionLevels() throws IOException {
        return getResolutionLevels(UNSPECIFIED);
    }

    @Override
    public double[][] getResolutionLevels(String coverageName) throws IOException {
        String name = checkUnspecifiedCoverage(coverageName);
        RasterManager manager = getRasterManager(name);
        return manager.levels;
    }

    @Override
    public List<HarvestedSource> harvest(String defaultCoverage, Object source, Hints hints)
            throws IOException, UnsupportedOperationException {
        // Get the HarvestedResource object associated to the source. This object defines the harvseting behaviour.
        HarvestedResource resource = HarvestedResource.getResourceFromObject(source);

        // Check if the source object can be accepted
        final List<HarvestedSource> result = new ArrayList<HarvestedSource>();
        if (resource == null) {
            result.add(new DefaultHarvestedSource(source, false, "Unrecognized source type"));
            return result;
        } else if (source instanceof File && !((File) source).exists() || singleFileList(source)) {
            result.add(new DefaultHarvestedSource(source, false,
                    "Specified file path does not exist"));
            return result;
        }
        // Harvesting of the input source
        resource.harvest(defaultCoverage, source, hints, result, this);

        return result;
    }

    /**
     * Simple method used for checking if the list contains a single object and it is a file
     *
     * @param source
     * @return
     */
    private boolean singleFileList(Object source) {
        if (source instanceof Collection<?>) {
            Collection<?> collection = ((Collection<?>) source);
            if(collection.size() == 1){
                // Selection of the single file
                File file = (File) collection.iterator().next();
                // Check if it exists
                if (!file.exists()) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public List<DimensionDescriptor> getDimensionDescriptors(String coverageName) throws IOException {
            RasterManager manager = getRasterManager(coverageName);
            return manager.getDimensionDescriptors();
    }

    @Override
    public void delete(boolean deleteData) throws IOException {
        // TODO: Should we make it synchronized?

        String[] coverageNames = getGridCoverageNames();
        for (String coverageName: coverageNames) {
            removeCoverage(coverageName, deleteData, true);
        }

        // Dispose before deleting to make sure any lock on files or resources is released
        dispose();
        removeDB();
        if (deleteData) {
            // quick way: delete everything
            final File[] list = parentDirectory.listFiles();
            for (File file: list) {
                FileUtils.deleteQuietly(file);
            }
        } else {
            finalizeCleanup();
        }
    }

    private void removeDB() throws IOException {
        final File parent = DataUtilities.urlToFile(sourceURL).getParentFile();

        final File datastoreProperties = new File(parent, "datastore.properties");
        if (datastoreProperties != null && datastoreProperties.exists() && datastoreProperties.canRead()) {
            CatalogManager.dropDatastore(datastoreProperties);
        }

        // Scan for MosaicConfigurationBeans from properties files
        List<MosaicConfigurationBean> beans = new ArrayList<MosaicConfigurationBean>();
       
    }

    /**
     * Finalize the clean up by removing any file returned by the cleanup filter.
     * Note that some H2 .db files change their name during life cycle. So they won't be stored inside the fileset manager
     */
    private void finalizeCleanup() {
        IOFileFilter filesFilter = Utils.getCleanupFilter();
        Collection<File> files = FileUtils.listFiles(parentDirectory, filesFilter, null);
        for (File file : files) {
            FileUtils.deleteQuietly(file);
        }

    }

    @Override
    public boolean removeCoverage(String coverageName) throws IOException, UnsupportedOperationException {
        return removeCoverage(coverageName, false);
    }
   
    /**
     * This subclass of the {@link ImageMosaicWalker} cycles around a List of files and for each one calls the superclass
     * handleFile() method. For each file is done a check if it really exists, it can be read and it is not a directory.
     *
     * @author Nicola Lagomarsini, GeoSolutions S.A.S.
     *
     */
    private static class ImageMosaicFileCollectionWalker extends ImageMosaicWalker{

        /** Input File list to walk on*/
        private Collection<File> files;

        public ImageMosaicFileCollectionWalker(ImageMosaicConfigHandler configHandler,
                ImageMosaicEventHandlers eventHandler, Collection<File> files) {
            super(configHandler, eventHandler);

            this.files = files;
        }

        @Override
        public void run() {
            try {
                // Initialization steps
                configHandler.indexingPreamble();
                startTransaction();
               
                // Setting of the Collection size
                setNumFiles(files.size());
               
                // Creation of an Iterator on the input files
                Iterator<File> it = files.iterator();
               
                // Cycle on all the input files
                while (it.hasNext()) {
                    File file = it.next();
                   
                    // Stop the Harvesting if requested
                    if (getStop()){
                        break;
                    }
                   
                    // Check if the File has an absolute path
                    if(checkFile(file)){
                        handleFile(file);
                    }else{
                        // SKIP and log
                        skipFile(file.getAbsolutePath());
                    }
                }
               
                // close transaction
                if (getStop()) {
                    rollbackTransaction();
                } else {
                    commitTransaction();
                }
               
            } catch (IOException e) {
                //Exception Logged
                LOGGER.log(Level.WARNING, e.getMessage(), e);
                try {
                    // Rollback of the Transaction
                    rollbackTransaction();
                } catch (IOException e1) {
                    throw new IllegalStateException(e);
                }
            }finally{
                // close transaction
                try {
                    closeTransaction();
                } catch (Exception e) {
                    final String message = "Unable to close transaction" + e.getLocalizedMessage();
                    if (LOGGER.isLoggable(Level.WARNING)) {
                        LOGGER.log(Level.WARNING, message, e);
                    }
                    // notify listeners
                    eventHandler.fireException(e);
                }

                // close indexing
                try {
                    configHandler.indexingPostamble(!getStop());
                } catch (Exception e) {
                    final String message = "Unable to close indexing" + e.getLocalizedMessage();
                    if (LOGGER.isLoggable(Level.WARNING)) {
                        LOGGER.log(Level.WARNING, message, e);
                    }
                    // notify listeners
                    eventHandler.fireException(e);
                }
            }
        }  
    }
}
TOP

Related Classes of org.geotools.gce.imagemosaic.ImageMosaicReader$ImageMosaicFileCollectionWalker

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.