org.geotools.gce.imagemosaic.ImageMosaicConfigHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.geotools.gce.imagemosaic.ImageMosaicConfigHandler.java

Source

/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 * 
 *    (C) 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.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.SampleModel;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.spi.ImageReaderSpi;
import javax.media.jai.ImageLayout;
import javax.xml.bind.JAXBException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.StructuredGridCoverage2DReader;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.factory.Hints;
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.index.DomainType;
import org.geotools.gce.imagemosaic.catalog.index.DomainsType;
import org.geotools.gce.imagemosaic.catalog.index.Indexer;
import org.geotools.gce.imagemosaic.catalog.index.Indexer.Collectors;
import org.geotools.gce.imagemosaic.catalog.index.Indexer.Collectors.Collector;
import org.geotools.gce.imagemosaic.catalog.index.Indexer.Coverages;
import org.geotools.gce.imagemosaic.catalog.index.Indexer.Coverages.Coverage;
import org.geotools.gce.imagemosaic.catalog.index.IndexerUtils;
import org.geotools.gce.imagemosaic.catalog.index.ParametersType;
import org.geotools.gce.imagemosaic.catalog.index.ParametersType.Parameter;
import org.geotools.gce.imagemosaic.catalog.index.SchemaType;
import org.geotools.gce.imagemosaic.catalog.index.SchemasType;
import org.geotools.gce.imagemosaic.catalogbuilder.CatalogBuilderConfiguration;
import org.geotools.gce.imagemosaic.catalogbuilder.MosaicBeanBuilder;
import org.geotools.gce.imagemosaic.properties.DefaultPropertiesCollectorSPI;
import org.geotools.gce.imagemosaic.properties.PropertiesCollector;
import org.geotools.gce.imagemosaic.properties.PropertiesCollectorFinder;
import org.geotools.gce.imagemosaic.properties.PropertiesCollectorSPI;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.util.Utilities;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

/**
 * This class is in responsible for creating and managing the catalog and the configuration of the mosaic
 * 
 * @author Carlo Cancellieri - GeoSolutions SAS
 * 
 * @source $URL$
 */
public class ImageMosaicConfigHandler {

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

    private List<PropertiesCollector> propertiesCollectors = null;

    private Map<String, MosaicConfigurationBean> configurations = new HashMap<String, MosaicConfigurationBean>();

    /**
     * Proper way to stop a thread is not by calling Thread.stop() but by using a shared variable that can be checked in order to notify a terminating
     * condition.
     */
    private volatile boolean stop = false;

    private GranuleCatalog catalog;

    private CatalogBuilderConfiguration runConfiguration;

    private ImageReaderSpi cachedReaderSPI;

    private ReferencedEnvelope imposedBBox;

    private ImageMosaicReader parentReader;

    private File indexerFile;

    private File parent;

    private ImageMosaicEventHandlers eventHandler;

    private boolean useExistingSchema;

    /**
     * Default constructor
     * 
     * @throws
     * @throws IllegalArgumentException
     */
    public ImageMosaicConfigHandler(final CatalogBuilderConfiguration configuration,
            final ImageMosaicEventHandlers eventHandler) {
        Utilities.ensureNonNull("runConfiguration", configuration);

        Utilities.ensureNonNull("eventHandler", eventHandler);
        this.eventHandler = eventHandler;

        Indexer defaultIndexer = configuration.getIndexer();
        ParametersType params = null;
        String rootMosaicDir = null;
        if (defaultIndexer != null) {
            params = defaultIndexer.getParameters();
            rootMosaicDir = IndexerUtils.getParam(params, Prop.ROOT_MOSAIC_DIR);
            IndexerUtils.getParameterAsBoolean(Prop.USE_EXISTING_SCHEMA, defaultIndexer);
        }

        Utilities.ensureNonNull("root location", rootMosaicDir);

        // look for and indexer.properties file
        parent = new File(rootMosaicDir);
        indexerFile = new File(parent, Utils.INDEXER_XML);
        Indexer indexer = null;

        Hints hints = configuration.getHints();
        String ancillaryFile = null;
        if (Utils.checkFileReadable(indexerFile)) {
            try {
                indexer = (Indexer) Utils.unmarshal(indexerFile);
                if (indexer != null) {
                    copyDefaultParams(params, indexer);
                }
            } catch (JAXBException e) {
                LOGGER.log(Level.WARNING, e.getMessage(), e);
            }
        } else {
            // Backward compatible with old indexing
            indexerFile = new File(parent, Utils.INDEXER_PROPERTIES);
            if (Utils.checkFileReadable(indexerFile)) {
                // load it and parse it
                final Properties props = Utils.loadPropertiesFromURL(DataUtilities.fileToURL(indexerFile));
                indexer = createIndexer(props, params);
            }
        }
        if (indexer != null) {
            // Overwrite default indexer only when indexer is available
            configuration.setIndexer(indexer);
            String param = IndexerUtils.getParameter(Utils.Prop.AUXILIARY_FILE, indexer);
            if (param != null) {
                ancillaryFile = param;
                setReader(hints, false);
            }
            if (IndexerUtils.getParameterAsBoolean(Utils.Prop.USE_EXISTING_SCHEMA, indexer)) {
                this.useExistingSchema = true;
            }
        }

        updateConfigurationHints(configuration, hints, ancillaryFile,
                IndexerUtils.getParam(params, Prop.ROOT_MOSAIC_DIR));

        // check config
        configuration.check();

        this.runConfiguration = new CatalogBuilderConfiguration(configuration);
    }

    private void setReader(Hints hints, final boolean updateHints) {
        if (hints != null && hints.containsKey(Utils.MOSAIC_READER)) {
            Object reader = hints.get(Utils.MOSAIC_READER);
            if (reader instanceof ImageMosaicReader) {
                if (getParentReader() == null) {
                    setParentReader((ImageMosaicReader) reader);
                }
                if (updateHints) {
                    Hints readerHints = getParentReader().getHints();
                    readerHints.add(hints);
                }
            }
        }

    }

    private void updateConfigurationHints(final CatalogBuilderConfiguration configuration, Hints hints,
            final String ancillaryFile, final String rootMosaicDir) {
        String ancillaryFilePath = null;
        if (ancillaryFile != null) {
            ancillaryFilePath = rootMosaicDir + File.separatorChar + ancillaryFile;
            if (hints != null) {
                hints.put(Utils.AUXILIARY_FILES_PATH, ancillaryFilePath);
            } else {
                hints = new Hints(Utils.AUXILIARY_FILES_PATH, ancillaryFilePath);
                configuration.setHints(hints);
            }
        }
        setReader(hints, true);
    }

    /**
     * Setup default params to the indexer.
     * 
     * @param params
     * @param indexer
     */
    private void copyDefaultParams(ParametersType params, Indexer indexer) {
        if (params != null) {
            List<Parameter> defaultParamList = params.getParameter();
            if (defaultParamList != null && !defaultParamList.isEmpty()) {
                ParametersType parameters = indexer.getParameters();
                if (parameters == null) {
                    parameters = Utils.OBJECT_FACTORY.createParametersType();
                    indexer.setParameters(parameters);
                }
                List<Parameter> parameterList = parameters.getParameter();
                for (Parameter defaultParameter : defaultParamList) {
                    final String defaultParameterName = defaultParameter.getName();
                    if (IndexerUtils.getParameter(defaultParameterName, indexer) == null) {
                        IndexerUtils.setParam(parameterList, defaultParameterName, defaultParameter.getValue());
                    }
                }
            }
        }
    }

    /**
     * Perform proper clean up.
     * 
     * <p>
     * Make sure to call this method when you are not running the {@link ImageMosaicConfigHandler} or bad things can happen. If it is running, please
     * stop it first.
     */
    public void reset() {
        eventHandler.removeAllProcessingEventListeners();
        // clear stop
        stop = false;
        closeIndexObjects();

        // fileIndex = 0;
        runConfiguration = null;

    }

    public boolean getStop() {
        return stop;
    }

    public void stop() {
        stop = true;
    }

    void indexingPreamble() throws IOException {

        //
        // create the index
        //
        catalog = CatalogManager.createCatalog(runConfiguration, !useExistingSchema);
        getParentReader().granuleCatalog = catalog;

        //
        // IMPOSED ENVELOPE
        //
        String bbox = runConfiguration.getParameter(Prop.ENVELOPE2D);
        try {
            this.imposedBBox = Utils.parseEnvelope(bbox);
        } catch (Exception e) {
            this.imposedBBox = null;
            if (LOGGER.isLoggable(Level.WARNING))
                LOGGER.log(Level.WARNING, "Unable to parse imposed bbox", e);
        }

        //
        // load property collectors
        //
        loadPropertyCollectors();
    }

    /**
     * Load properties collectors from the configuration
     */
    private void loadPropertyCollectors() {
        // load property collectors
        Indexer indexer = runConfiguration.getIndexer();
        Collectors collectors = indexer.getCollectors();
        if (collectors == null) {
            if (LOGGER.isLoggable(Level.FINE)) {
                LOGGER.fine("No properties collector have been found");
            }
            return;
        }
        List<Collector> collectorList = collectors.getCollector();

        // load the SPI set
        final Set<PropertiesCollectorSPI> pcSPIs = PropertiesCollectorFinder.getPropertiesCollectorSPI();

        // parse the string
        final List<PropertiesCollector> pcs = new ArrayList<PropertiesCollector>();
        for (Collector collector : collectorList) {
            PropertiesCollectorSPI selectedSPI = null;
            final String spiName = collector.getSpi();
            for (PropertiesCollectorSPI spi : pcSPIs) {
                if (spi.isAvailable() && spi.getName().equalsIgnoreCase(spiName)) {
                    selectedSPI = spi;
                    break;
                }
            }

            if (selectedSPI == null) {
                if (LOGGER.isLoggable(Level.INFO)) {
                    LOGGER.info("Unable to find a PropertyCollector for this definition: " + spiName);
                }
                continue;
            }

            // property names
            String collectorValue = collector.getValue();
            final String config;
            if (!collectorValue.startsWith(DefaultPropertiesCollectorSPI.REGEX_PREFIX)) {
                config = DefaultPropertiesCollectorSPI.REGEX_PREFIX + collector.getValue();
            } else {
                config = collector.getValue();
            }

            // create the PropertiesCollector
            final PropertiesCollector pc = selectedSPI.create(config, Arrays.asList(collector.getMapped()));
            if (pc != null) {
                pcs.add(pc);
            } else {
                if (LOGGER.isLoggable(Level.INFO)) {
                    LOGGER.info("Unable to create PropertyCollector");
                }
            }
        }
        this.propertiesCollectors = pcs;
    }

    void indexingPostamble(final boolean success) throws IOException {
        // close shapefile elements
        if (success) {
            Indexer indexer = runConfiguration.getIndexer();
            boolean supportsEmpty = false;
            if (indexer != null) {
                supportsEmpty = IndexerUtils.getParameterAsBoolean(Prop.CAN_BE_EMPTY, indexer);
            }
            // complete initialization of mosaic configuration
            boolean haveConfigs = configurations != null && !configurations.isEmpty();
            if (haveConfigs || supportsEmpty) {

                // We did found some MosaicConfigurations
                Set<String> keys = configurations.keySet();
                int keySize = keys.size();
                if (haveConfigs || !supportsEmpty) {
                    final boolean useName = keySize > 1;
                    for (String key : keys) {
                        MosaicConfigurationBean mosaicConfiguration = configurations.get(key);
                        RasterManager manager = parentReader.getRasterManager(key);
                        manager.initialize(supportsEmpty);
                        // create sample image if the needed elements are available
                        createSampleImage(mosaicConfiguration, useName);
                        eventHandler.fireEvent(Level.INFO, "Creating final properties file ", 99.9);
                        createPropertiesFiles(mosaicConfiguration);
                    }
                }
                final String base = FilenameUtils.getName(parent.getAbsolutePath());
                // we create a root properties file if we have more than one coverage, or if the
                // one coverage does not have the default name
                if (supportsEmpty || keySize > 1 || (keySize > 0 && !base.equals(keys.iterator().next()))) {
                    File mosaicFile = null;
                    File originFile = null;
                    if (indexerFile.getAbsolutePath().endsWith("xml")) {
                        mosaicFile = new File(
                                indexerFile.getAbsolutePath().replace(Utils.INDEXER_XML, (base + ".xml")));
                        originFile = indexerFile;
                    } else if (indexerFile.getAbsolutePath().endsWith("properties")) {
                        mosaicFile = new File(indexerFile.getAbsolutePath().replace(Utils.INDEXER_PROPERTIES,
                                (base + ".properties")));
                        originFile = indexerFile;
                    } else {
                        final String source = runConfiguration.getParameter(Prop.ROOT_MOSAIC_DIR)
                                + File.separatorChar + configurations.get(keys.iterator().next()).getName()
                                + ".properties";
                        mosaicFile = new File(indexerFile.getAbsolutePath().replace(Utils.INDEXER_PROPERTIES,
                                (base + ".properties")));
                        originFile = new File(source);
                    }
                    if (!mosaicFile.exists()) {
                        FileUtils.copyFile(originFile, mosaicFile);
                    }
                }

                // processing information
                eventHandler.fireEvent(Level.FINE, "Done!!!", 100);

            } else {
                // processing information
                eventHandler.fireEvent(Level.FINE, "Nothing to process!!!", 100);
            }
        } else {
            // processing information
            eventHandler.fireEvent(Level.FINE, "Canceled!!!", 100);
        }
        closeIndexObjects();
    }

    /**
     * Store a sample image frmo which we can derive the default SM and CM
     */
    private void createSampleImage(final MosaicConfigurationBean mosaicConfiguration, final boolean useName) {
        // create a sample image to store SM and CM
        Utilities.ensureNonNull("mosaicConfiguration", mosaicConfiguration);
        String filePath = null;
        if (mosaicConfiguration.getSampleModel() != null && mosaicConfiguration.getColorModel() != null) {

            // sample image file
            // TODO: Consider revisit this using different name/folder
            final String baseName = runConfiguration.getParameter(Prop.ROOT_MOSAIC_DIR) + "/";
            filePath = baseName + (useName ? mosaicConfiguration.getName() : "") + Utils.SAMPLE_IMAGE_NAME;
            try {
                Utils.storeSampleImage(new File(filePath), mosaicConfiguration.getSampleModel(),
                        mosaicConfiguration.getColorModel());
            } catch (IOException e) {
                eventHandler.fireEvent(Level.SEVERE, e.getLocalizedMessage(), 0);
            }
        }
    }

    private Indexer createIndexer(Properties props, ParametersType params) {
        // Initializing Indexer objects
        Indexer indexer = Utils.OBJECT_FACTORY.createIndexer();
        indexer.setParameters(params != null ? params : Utils.OBJECT_FACTORY.createParametersType());
        Coverages coverages = Utils.OBJECT_FACTORY.createIndexerCoverages();
        indexer.setCoverages(coverages);
        List<Coverage> coverageList = coverages.getCoverage();

        Coverage coverage = Utils.OBJECT_FACTORY.createIndexerCoveragesCoverage();
        coverageList.add(coverage);

        indexer.setParameters(params);
        List<Parameter> parameters = params.getParameter();

        // name
        if (props.containsKey(Prop.NAME)) {
            IndexerUtils.setParam(parameters, props, Prop.NAME);
            coverage.setName(props.getProperty(Prop.NAME));
        }

        // absolute
        if (props.containsKey(Prop.ABSOLUTE_PATH))
            IndexerUtils.setParam(parameters, props, Prop.ABSOLUTE_PATH);

        // recursive
        if (props.containsKey(Prop.RECURSIVE))
            IndexerUtils.setParam(parameters, props, Prop.RECURSIVE);

        // wildcard
        if (props.containsKey(Prop.WILDCARD))
            IndexerUtils.setParam(parameters, props, Prop.WILDCARD);

        // schema
        if (props.containsKey(Prop.SCHEMA)) {
            SchemasType schemas = Utils.OBJECT_FACTORY.createSchemasType();
            SchemaType schema = Utils.OBJECT_FACTORY.createSchemaType();
            indexer.setSchemas(schemas);
            schemas.getSchema().add(schema);
            schema.setAttributes(props.getProperty(Prop.SCHEMA));
            schema.setName(IndexerUtils.getParameter(Prop.INDEX_NAME, indexer));
        }

        DomainsType domains = coverage.getDomains();
        List<DomainType> domainList = null;
        // time attr
        if (props.containsKey(Prop.TIME_ATTRIBUTE)) {
            if (domains == null) {
                domains = Utils.OBJECT_FACTORY.createDomainsType();
                coverage.setDomains(domains);
                domainList = domains.getDomain();
            }
            DomainType domain = Utils.OBJECT_FACTORY.createDomainType();
            domain.setName(Utils.TIME_DOMAIN.toLowerCase());
            IndexerUtils.setAttributes(domain, props.getProperty(Prop.TIME_ATTRIBUTE));
            domainList.add(domain);
        }

        // elevation attr
        if (props.containsKey(Prop.ELEVATION_ATTRIBUTE)) {
            if (domains == null) {
                domains = Utils.OBJECT_FACTORY.createDomainsType();
                coverage.setDomains(domains);
                domainList = domains.getDomain();
            }
            DomainType domain = Utils.OBJECT_FACTORY.createDomainType();
            domain.setName(Utils.ELEVATION_DOMAIN.toLowerCase());
            IndexerUtils.setAttributes(domain, props.getProperty(Prop.ELEVATION_ATTRIBUTE));
            domainList.add(domain);
        }

        // Additional domain attr
        if (props.containsKey(Prop.ADDITIONAL_DOMAIN_ATTRIBUTES)) {
            if (domains == null) {
                domains = Utils.OBJECT_FACTORY.createDomainsType();
                coverage.setDomains(domains);
                domainList = domains.getDomain();
            }
            String attributes = props.getProperty(Prop.ADDITIONAL_DOMAIN_ATTRIBUTES);
            IndexerUtils.parseAdditionalDomains(attributes, domainList);
        }

        // imposed BBOX
        if (props.containsKey(Prop.ENVELOPE2D))
            IndexerUtils.setParam(parameters, props, Prop.ENVELOPE2D);

        // imposed Pyramid Layout
        if (props.containsKey(Prop.RESOLUTION_LEVELS))
            IndexerUtils.setParam(parameters, props, Prop.RESOLUTION_LEVELS);

        // collectors
        if (props.containsKey(Prop.PROPERTY_COLLECTORS)) {
            IndexerUtils.setPropertyCollectors(indexer, props.getProperty(Prop.PROPERTY_COLLECTORS));
        }

        if (props.containsKey(Prop.CACHING))
            IndexerUtils.setParam(parameters, props, Prop.CACHING);

        if (props.containsKey(Prop.ROOT_MOSAIC_DIR)) {
            // Overriding root mosaic directory
            IndexerUtils.setParam(parameters, props, Prop.ROOT_MOSAIC_DIR);
        }

        if (props.containsKey(Prop.INDEXING_DIRECTORIES)) {
            IndexerUtils.setParam(parameters, props, Prop.INDEXING_DIRECTORIES);
        }
        if (props.containsKey(Prop.AUXILIARY_FILE)) {
            IndexerUtils.setParam(parameters, props, Prop.AUXILIARY_FILE);
        }
        if (props.containsKey(Prop.CAN_BE_EMPTY)) {
            IndexerUtils.setParam(parameters, props, Prop.CAN_BE_EMPTY);
        }
        if (props.containsKey(Prop.USE_EXISTING_SCHEMA)) {
            IndexerUtils.setParam(parameters, props, Prop.USE_EXISTING_SCHEMA);
        }
        if (props.containsKey(Prop.CHECK_AUXILIARY_METADATA)) {
            IndexerUtils.setParam(parameters, props, Prop.CHECK_AUXILIARY_METADATA);
        }
        return indexer;
    }

    private void closeIndexObjects() {

        //        // TODO: We may consider avoid disposing the catalog to allow the reader to use the already available catalog
        //        try {
        //            if (catalog != null) {
        //                catalog.dispose();
        //            }
        //        } catch (Throwable e) {
        //            LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
        //        }
        //
        //        catalog = null;
        //        getParentReader().granuleCatalog = null;
    }

    /**
     * Creates the final properties file.
     */
    private void createPropertiesFiles(MosaicConfigurationBean mosaicConfiguration) {

        //
        // FINAL STEP
        //
        // CREATING GENERAL INFO FILE
        //

        CatalogConfigurationBean catalogConfigurationBean = mosaicConfiguration.getCatalogConfigurationBean();

        // envelope
        final Properties properties = new Properties();
        properties.setProperty(Utils.Prop.ABSOLUTE_PATH,
                Boolean.toString(catalogConfigurationBean.isAbsolutePath()));
        properties.setProperty(Utils.Prop.LOCATION_ATTRIBUTE, catalogConfigurationBean.getLocationAttribute());

        // Time
        final String timeAttribute = mosaicConfiguration.getTimeAttribute();
        if (timeAttribute != null) {
            properties.setProperty(Utils.Prop.TIME_ATTRIBUTE, mosaicConfiguration.getTimeAttribute());
        }

        // Elevation
        final String elevationAttribute = mosaicConfiguration.getElevationAttribute();
        if (elevationAttribute != null) {
            properties.setProperty(Utils.Prop.ELEVATION_ATTRIBUTE, mosaicConfiguration.getElevationAttribute());
        }

        // Additional domains
        final String additionalDomainAttribute = mosaicConfiguration.getAdditionalDomainAttributes();
        if (additionalDomainAttribute != null) {
            properties.setProperty(Utils.Prop.ADDITIONAL_DOMAIN_ATTRIBUTES,
                    mosaicConfiguration.getAdditionalDomainAttributes());
        }

        final int numberOfLevels = mosaicConfiguration.getLevelsNum();
        final double[][] resolutionLevels = mosaicConfiguration.getLevels();
        properties.setProperty(Utils.Prop.LEVELS_NUM, Integer.toString(numberOfLevels));
        final StringBuilder levels = new StringBuilder();
        for (int k = 0; k < numberOfLevels; k++) {
            levels.append(Double.toString(resolutionLevels[k][0])).append(",")
                    .append(Double.toString(resolutionLevels[k][1]));
            if (k < numberOfLevels - 1) {
                levels.append(" ");
            }
        }
        properties.setProperty(Utils.Prop.LEVELS, levels.toString());
        properties.setProperty(Utils.Prop.NAME, mosaicConfiguration.getName());
        properties.setProperty(Utils.Prop.TYPENAME, mosaicConfiguration.getName());
        properties.setProperty(Utils.Prop.EXP_RGB, Boolean.toString(mosaicConfiguration.isExpandToRGB()));
        properties.setProperty(Utils.Prop.CHECK_AUXILIARY_METADATA,
                Boolean.toString(mosaicConfiguration.isCheckAuxiliaryMetadata()));
        properties.setProperty(Utils.Prop.HETEROGENEOUS,
                Boolean.toString(catalogConfigurationBean.isHeterogeneous()));

        if (cachedReaderSPI != null) {
            // suggested spi
            properties.setProperty(Utils.Prop.SUGGESTED_SPI, cachedReaderSPI.getClass().getName());
        }

        // write down imposed bbox
        if (imposedBBox != null) {
            properties.setProperty(Utils.Prop.ENVELOPE2D, imposedBBox.getMinX() + "," + imposedBBox.getMinY() + " "
                    + imposedBBox.getMaxX() + "," + imposedBBox.getMaxY());
        }
        properties.setProperty(Utils.Prop.CACHING, Boolean.toString(catalogConfigurationBean.isCaching()));
        if (mosaicConfiguration.getAuxiliaryFilePath() != null) {
            properties.setProperty(Utils.Prop.AUXILIARY_FILE, mosaicConfiguration.getAuxiliaryFilePath());
        }

        OutputStream outStream = null;
        String filePath = runConfiguration.getParameter(Prop.ROOT_MOSAIC_DIR) + "/"
        // + runConfiguration.getIndexName() + ".properties"));
                + mosaicConfiguration.getName() + ".properties";
        try {
            outStream = new BufferedOutputStream(new FileOutputStream(filePath));
            properties.store(outStream, "-Automagically created from GeoTools-");
        } catch (FileNotFoundException e) {
            eventHandler.fireEvent(Level.SEVERE, e.getLocalizedMessage(), 0);
        } catch (IOException e) {
            eventHandler.fireEvent(Level.SEVERE, e.getLocalizedMessage(), 0);
        } finally {
            if (outStream != null) {
                IOUtils.closeQuietly(outStream);
            }
        }
    }

    /**
     * Check whether the specified coverage already exist in the reader. This allows to get the rasterManager associated with that coverage instead of
     * creating a new one.
     * 
     * @param coverageName the name of the coverage to be searched
     * @return {@code true} in case that coverage already exists
     * @throws IOException
     */
    protected boolean coverageExists(String coverageName) throws IOException {
        String[] coverages = getParentReader().getGridCoverageNames();
        for (String coverage : coverages) {
            if (coverage.equals(coverageName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Use the passed coverageReader to create or update the all the needed configurations<br/>
     * It not responsible of the passed coverageReader which should be disposed outside (in the caller).
     * 
     * @param coverageReader
     * @param inputCoverageName
     * @param fileBeingProcessed
     * @param fileIndex
     * @param numFiles
     * @param transaction
     * @throws IOException
     */
    public void updateConfiguration(GridCoverage2DReader coverageReader, final String inputCoverageName,
            File fileBeingProcessed, int fileIndex, double numFiles, DefaultTransaction transaction)
            throws IOException {

        final String indexName = getRunConfiguration().getParameter(Prop.INDEX_NAME);
        final String coverageName = coverageReader instanceof StructuredGridCoverage2DReader ? inputCoverageName
                : indexName;

        final Indexer indexer = getRunConfiguration().getIndexer();

        // checking whether the coverage already exists
        final boolean coverageExists = coverageExists(coverageName);
        MosaicConfigurationBean mosaicConfiguration = null;
        MosaicConfigurationBean currentConfigurationBean = null;
        RasterManager rasterManager = null;
        if (coverageExists) {

            // Get the manager for this coverage so it can be updated
            rasterManager = getParentReader().getRasterManager(coverageName);
            mosaicConfiguration = rasterManager.getConfiguration();
        }

        // STEP 2
        // Collecting all Coverage properties to setup a MosaicConfigurationBean through
        // the builder
        final MosaicBeanBuilder configBuilder = new MosaicBeanBuilder();

        final GeneralEnvelope envelope = (GeneralEnvelope) coverageReader.getOriginalEnvelope(inputCoverageName);
        final CoordinateReferenceSystem actualCRS = coverageReader.getCoordinateReferenceSystem(inputCoverageName);

        SampleModel sm = null;
        ColorModel cm = null;
        int numberOfLevels = 1;
        double[][] resolutionLevels = null;
        CatalogBuilderConfiguration catalogConfig;
        if (mosaicConfiguration == null) {
            catalogConfig = getRunConfiguration();
            // We don't have a configuration for this configuration

            // Get the type specifier for this image and the check that the
            // image has the correct sample model and color model.
            // If this is the first cycle of the loop we initialize everything.
            //
            ImageLayout layout = coverageReader.getImageLayout(inputCoverageName);
            cm = layout.getColorModel(null);
            sm = layout.getSampleModel(null);
            numberOfLevels = coverageReader.getNumOverviews(inputCoverageName) + 1;
            resolutionLevels = coverageReader.getResolutionLevels(inputCoverageName);

            // at the first step we initialize everything that we will
            // reuse afterwards starting with color models, sample
            // models, crs, etc....

            configBuilder.setSampleModel(sm);
            configBuilder.setColorModel(cm);
            ColorModel defaultCM = cm;

            // Checking palette
            if (defaultCM instanceof IndexColorModel) {
                IndexColorModel icm = (IndexColorModel) defaultCM;
                int numBands = defaultCM.getNumColorComponents();
                byte[][] defaultPalette = new byte[3][icm.getMapSize()];
                icm.getReds(defaultPalette[0]);
                icm.getGreens(defaultPalette[0]);
                icm.getBlues(defaultPalette[0]);
                if (numBands == 4) {
                    icm.getAlphas(defaultPalette[0]);
                }
                configBuilder.setPalette(defaultPalette);
            }

            // STEP 2.A
            // Preparing configuration
            configBuilder.setCrs(actualCRS);
            configBuilder.setLevels(resolutionLevels);
            configBuilder.setLevelsNum(numberOfLevels);
            configBuilder.setName(coverageName);
            configBuilder.setTimeAttribute(IndexerUtils.getAttribute(coverageName, Utils.TIME_DOMAIN, indexer));
            configBuilder.setElevationAttribute(
                    IndexerUtils.getAttribute(coverageName, Utils.ELEVATION_DOMAIN, indexer));
            configBuilder.setAdditionalDomainAttributes(
                    IndexerUtils.getAttribute(coverageName, Utils.ADDITIONAL_DOMAIN, indexer));

            final Hints runHints = getRunConfiguration().getHints();
            if (runHints != null && runHints.containsKey(Utils.AUXILIARY_FILES_PATH)) {
                String auxiliaryFilePath = (String) runHints.get(Utils.AUXILIARY_FILES_PATH);
                if (auxiliaryFilePath != null && auxiliaryFilePath.trim().length() > 0) {
                    configBuilder.setAuxiliaryFilePath(auxiliaryFilePath);
                }
            }

            final CatalogConfigurationBean catalogConfigurationBean = new CatalogConfigurationBean();
            catalogConfigurationBean.setCaching(IndexerUtils.getParameterAsBoolean(Prop.CACHING, indexer));
            catalogConfigurationBean
                    .setAbsolutePath(IndexerUtils.getParameterAsBoolean(Prop.ABSOLUTE_PATH, indexer));

            catalogConfigurationBean
                    .setLocationAttribute(IndexerUtils.getParameter(Prop.LOCATION_ATTRIBUTE, indexer));

            catalogConfigurationBean.setTypeName(coverageName);

            configBuilder.setCatalogConfigurationBean(catalogConfigurationBean);
            configBuilder.setCheckAuxiliaryMetadata(
                    IndexerUtils.getParameterAsBoolean(Prop.CHECK_AUXILIARY_METADATA, indexer));

            currentConfigurationBean = configBuilder.getMosaicConfigurationBean();

            // Creating a rasterManager which will be initialized after populating the catalog
            rasterManager = getParentReader().addRasterManager(currentConfigurationBean, false);

            // Creating a granuleStore
            if (!useExistingSchema) {
                // creating the schema
                SimpleFeatureType indexSchema = CatalogManager.createSchema(getRunConfiguration(),
                        currentConfigurationBean.getName(), actualCRS);
                getParentReader().createCoverage(coverageName, indexSchema);
                //            } else {
                //                rasterManager.typeName = coverageName;
            }
            getConfigurations().put(currentConfigurationBean.getName(), currentConfigurationBean);

        } else {
            catalogConfig = new CatalogBuilderConfiguration();
            CatalogConfigurationBean bean = mosaicConfiguration.getCatalogConfigurationBean();
            catalogConfig.setParameter(Prop.LOCATION_ATTRIBUTE, (bean.getLocationAttribute()));
            catalogConfig.setParameter(Prop.ABSOLUTE_PATH, Boolean.toString(bean.isAbsolutePath()));
            catalogConfig.setParameter(Prop.ROOT_MOSAIC_DIR/* setRootMosaicDirectory( */,
                    getRunConfiguration().getParameter(Prop.ROOT_MOSAIC_DIR));

            // We already have a Configuration for this coverage.
            // Check its properties are compatible with the existing coverage.

            CatalogConfigurationBean catalogConfigurationBean = bean;
            if (!catalogConfigurationBean.isHeterogeneous()) {

                // There is no need to check resolutions if the mosaic
                // has been already marked as heterogeneous

                numberOfLevels = coverageReader.getNumOverviews(inputCoverageName) + 1;
                boolean needUpdate = false;

                //
                // Heterogeneousity check
                //
                if (numberOfLevels != mosaicConfiguration.getLevelsNum()) {
                    catalogConfigurationBean.setHeterogeneous(true);
                    if (numberOfLevels > mosaicConfiguration.getLevelsNum()) {
                        resolutionLevels = coverageReader.getResolutionLevels(inputCoverageName);
                        mosaicConfiguration.setLevels(resolutionLevels);
                        mosaicConfiguration.setLevelsNum(numberOfLevels);
                        needUpdate = true;
                    }
                } else {
                    final double[][] mosaicLevels = mosaicConfiguration.getLevels();
                    resolutionLevels = coverageReader.getResolutionLevels(inputCoverageName);
                    final boolean homogeneousLevels = Utils.homogeneousCheck(numberOfLevels, resolutionLevels,
                            mosaicLevels);
                    if (!homogeneousLevels) {
                        catalogConfigurationBean.setHeterogeneous(true);
                        needUpdate = true;
                    }
                }
                // configuration need to be updated
                if (needUpdate) {
                    getConfigurations().put(mosaicConfiguration.getName(), mosaicConfiguration);
                }
            }
            ImageLayout layout = coverageReader.getImageLayout(inputCoverageName);
            cm = layout.getColorModel(null);
            sm = layout.getSampleModel(null);

            // comparing ColorModel
            // comparing SampeModel
            // comparing CRSs
            ColorModel actualCM = cm;
            CoordinateReferenceSystem expectedCRS;
            if (mosaicConfiguration.getCrs() != null) {
                expectedCRS = mosaicConfiguration.getCrs();
            } else {
                expectedCRS = rasterManager.spatialDomainManager.coverageCRS;
            }
            if (!(CRS.equalsIgnoreMetadata(expectedCRS, actualCRS))) {
                // if ((fileIndex > 0 ? !(CRS.equalsIgnoreMetadata(defaultCRS, actualCRS)) : false)) {
                eventHandler.fireFileEvent(Level.INFO, fileBeingProcessed, false,
                        "Skipping image " + fileBeingProcessed + " because CRSs do not match.",
                        (((fileIndex + 1) * 99.0) / numFiles));
                return;
            }

            byte[][] palette = mosaicConfiguration.getPalette();
            ColorModel colorModel = mosaicConfiguration.getColorModel();
            if (colorModel == null) {
                palette = rasterManager.getConfiguration().getPalette();
                colorModel = rasterManager.defaultCM;
            }
            if (Utils.checkColorModels(colorModel, palette, mosaicConfiguration, actualCM)) {
                // if (checkColorModels(defaultCM, defaultPalette, actualCM)) {
                eventHandler.fireFileEvent(Level.INFO, fileBeingProcessed, false,
                        "Skipping image " + fileBeingProcessed + " because color models do not match.",
                        (((fileIndex + 1) * 99.0) / numFiles));
                return;
            }

        }
        // STEP 3
        if (!useExistingSchema) {
            // create and store features
            CatalogManager.updateCatalog(coverageName, fileBeingProcessed, coverageReader, getParentReader(),
                    catalogConfig, envelope, transaction, getPropertiesCollectors());
        }
    }

    public void dispose() {
        reset();
    }

    public Map<String, MosaicConfigurationBean> getConfigurations() {
        return configurations;
    }

    public GranuleCatalog getCatalog() {
        return catalog;
    }

    public CatalogBuilderConfiguration getRunConfiguration() {
        return runConfiguration;
    }

    public ImageMosaicReader getParentReader() {
        return parentReader;
    }

    public void setParentReader(ImageMosaicReader parentReader) {
        this.parentReader = parentReader;
    }

    public List<PropertiesCollector> getPropertiesCollectors() {
        return propertiesCollectors;
    }

    public boolean isUseExistingSchema() {
        return useExistingSchema;
    }

}