Package org.geotools.gce.imagemosaic

Source Code of org.geotools.gce.imagemosaic.RasterManager$DomainDescriptor

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2007-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.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.ColorModel;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.media.jai.ImageLayout;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.DecimationPolicy;
import org.geotools.coverage.grid.io.DefaultDimensionDescriptor;
import org.geotools.coverage.grid.io.DimensionDescriptor;
import org.geotools.coverage.grid.io.GranuleSource;
import org.geotools.coverage.grid.io.GranuleStore;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.GridFormatFinder;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.coverage.grid.io.StructuredGridCoverage2DReader;
import org.geotools.data.DataUtilities;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.factory.Hints;
import org.geotools.feature.visitor.CalcResult;
import org.geotools.feature.visitor.FeatureCalc;
import org.geotools.feature.visitor.MaxVisitor;
import org.geotools.feature.visitor.MinVisitor;
import org.geotools.feature.visitor.UniqueVisitor;
import org.geotools.filter.SortByImpl;
import org.geotools.gce.imagemosaic.OverviewsController.OverviewLevel;
import org.geotools.gce.imagemosaic.catalog.CatalogConfigurationBean;
import org.geotools.gce.imagemosaic.catalog.GranuleCatalog;
import org.geotools.gce.imagemosaic.catalog.GranuleCatalogSource;
import org.geotools.gce.imagemosaic.catalog.GranuleCatalogStore;
import org.geotools.gce.imagemosaic.catalog.GranuleCatalogVisitor;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.parameter.DefaultParameterDescriptor;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.builder.GridToEnvelopeMapper;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.geotools.referencing.operation.transform.IdentityTransform;
import org.geotools.referencing.operation.transform.ProjectiveTransform;
import org.geotools.resources.coverage.CoverageUtilities;
import org.geotools.resources.coverage.FeatureUtilities;
import org.geotools.resources.image.ImageUtilities;
import org.geotools.util.Range;
import org.geotools.util.Utilities;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.sort.SortOrder;
import org.opengis.geometry.BoundingBox;
import org.opengis.geometry.Envelope;
import org.opengis.metadata.Identifier;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.ReferenceIdentifier;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransform2D;
import org.opengis.referencing.operation.TransformException;

/**
*
* @author Simone Giannecchini, GeoSolutions SAS
* @author Daniele Romagnoli, GeoSolutions SAS
*
*/
@SuppressWarnings({"rawtypes","unchecked"})
public class RasterManager {

    final Hints excludeMosaicHints = new Hints(Utils.EXCLUDE_MOSAIC, true);

    /**
     * This class is responsible for putting together all the 2D spatial information needed for a certain raster.
     *
     * <p>
     * Notice that when this structure will be extended to work in ND this will become much more complex or as an
     * alternative a sibling TemporalDomainManager will be created.
     *
     * @author Simone Giannecchini, GeoSolutions SAS
     *
     */
    static class SpatialDomainManager{

        /** The base envelope 2D */
       ReferencedEnvelope coverageBBox;
      
       /** The CRS for the coverage */
       CoordinateReferenceSystem coverageCRS;
      
       /** The CRS related to the base envelope 2D */
       CoordinateReferenceSystem coverageCRS2D;
       // ////////////////////////////////////////////////////////////////////////
       //
       // Base coverage properties
       //
       // ////////////////////////////////////////////////////////////////////////
       /** The base envelope read from file */
       GeneralEnvelope coverageEnvelope = null;
      
       double[] coverageFullResolution;
      
       /** WGS84 envelope 2D for this coverage */
       ReferencedEnvelope coverageGeographicBBox;
      
       CoordinateReferenceSystem coverageGeographicCRS2D;
      
       MathTransform2D coverageGridToWorld2D;
      
       /** The base grid range for the coverage */
       Rectangle coverageRasterArea;
      
       GridEnvelope gridEnvelope;

       public SpatialDomainManager(final GeneralEnvelope envelope,
                       final GridEnvelope2D coverageGridrange,
                       final CoordinateReferenceSystem crs,
                       final MathTransform coverageGridToWorld2D,
                       final OverviewsController overviewsController) throws TransformException, FactoryException {
           this.coverageEnvelope = envelope.clone();
           this.gridEnvelope = coverageGridrange.clone();
           this.coverageRasterArea = (Rectangle) gridEnvelope;
           this.coverageCRS = crs;
           this.coverageGridToWorld2D = (MathTransform2D) coverageGridToWorld2D;
           this.coverageFullResolution = new double[2];
           final OverviewLevel highestLevel= overviewsController.resolutionsLevels.get(0);
           coverageFullResolution[0] = highestLevel.resolutionX;
           coverageFullResolution[1] = highestLevel.resolutionY;
          
           prepareCoverageSpatialElements();
       }

        /**
         * Initialize the 2D properties (CRS and Envelope) of this coverage
         *
         * @throws TransformException
         *
         * @throws FactoryException
         * @throws TransformException
         * @throws FactoryException
         */
        private void prepareCoverageSpatialElements() throws TransformException, FactoryException {
            //
            // basic initialization
            //
            coverageGeographicBBox = ImageUtilities.getWGS84ReferencedEnvelope(coverageEnvelope);
            coverageGeographicCRS2D = coverageGeographicBBox != null ? coverageGeographicBBox
                    .getCoordinateReferenceSystem() : null;

            //
            // Get the original envelope 2d and its spatial reference system
            //
            coverageCRS2D = CRS.getHorizontalCRS(coverageCRS);
            assert coverageCRS2D.getCoordinateSystem().getDimension() == 2;
            if (coverageCRS.getCoordinateSystem().getDimension() != 2) {
                final MathTransform transform = CRS.findMathTransform(coverageCRS,
                        (CoordinateReferenceSystem) coverageCRS2D);
                final GeneralEnvelope bbox = CRS.transform(transform, coverageEnvelope);
                bbox.setCoordinateReferenceSystem(coverageCRS2D);
                coverageBBox = new ReferencedEnvelope(bbox);
            } else {
                // it is already a bbox
                coverageBBox = new ReferencedEnvelope(coverageEnvelope);
            }
        }
       
        public MathTransform getOriginalGridToWorld(final PixelInCell pixInCell) {
            synchronized (this) {
                if (coverageGridToWorld2D == null) {
                    final GridToEnvelopeMapper geMapper = new GridToEnvelopeMapper(gridEnvelope,
                            coverageEnvelope);
                    geMapper.setPixelAnchor(PixelInCell.CELL_CENTER);
                    coverageGridToWorld2D = (MathTransform2D) geMapper.createTransform();
                }
            }

            // we do not have to change the pixel datum
            if (pixInCell == PixelInCell.CELL_CENTER)
                return coverageGridToWorld2D;

            // we do have to change the pixel datum
            if (coverageGridToWorld2D instanceof AffineTransform) {
                final AffineTransform tr = new AffineTransform(
                        (AffineTransform) coverageGridToWorld2D);
                tr.concatenate(AffineTransform.getTranslateInstance(-0.5, -0.5));
                return ProjectiveTransform.create(tr);
            }
            if (coverageGridToWorld2D instanceof IdentityTransform) {
                final AffineTransform tr = new AffineTransform(1, 0, 0, 1, 0, 0);
                tr.concatenate(AffineTransform.getTranslateInstance(-0.5, -0.5));
                return ProjectiveTransform.create(tr);
            }
            throw new IllegalStateException("This reader's grid to world transform is invalud!");
        }
    }

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

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

    /**
     * {@link DomainDescriptor} describe a single domain in terms of name and {@link ParameterDescriptor} that can be used to filter values during a
     * read operation.
     *
     * <p>
     * Notice that there is no caching of values for the domain itself right now.
     *
     * <p>
     * The domain must have unique identifiers.
     *
     * @author Simone Giannecchini, GeoSolutions SAS
     *
     */
    class DomainDescriptor {

        static final String DOMAIN_SUFFIX = "_DOMAIN";

        static final String HAS_PREFIX = "HAS_";

        static final String DATATYPE_SUFFIX = "_DATATYPE";

        private DomainType domainType = DomainType.SINGLE_VALUE;

        /** Unique identifier for this domain. */
        private final String identifier;

        /** propertyName for this domain that tells me which Property from the underlying catalog provides values for it. */
        private final String propertyName;

        /** additionalPropertyName for this domain. It won't be null ONLY in case of ranged domains. */
        private final String additionalPropertyName;

        /** domain dataType */
        private final String dataType;

        /** The {@link ParameterDescriptor} that can be used to filter on this domain during a read operation. */
        private final DefaultParameterDescriptor<List> domainParameterDescriptor;

        /**
         * @return the identifier
         */
        private String getIdentifier() {
            return identifier;
        }

        public boolean isHasRanges() {
            return additionalPropertyName != null;
        }

        public String getDataType() {
            return dataType;
        }

        /**
         * @return the domainaParameterDescriptor
         */
        private DefaultParameterDescriptor<List> getDomainaParameterDescriptor() {
            return domainParameterDescriptor;
        }

        private DomainDescriptor(final String identifier, final DomainType domainType, final String dataType,
                final String propertyName, final String additionalPropertyName) {
            this.identifier = identifier;
            this.propertyName = propertyName;
            this.domainType = domainType;
            this.dataType = dataType;
            this.additionalPropertyName = additionalPropertyName;
            final String name = identifier.toUpperCase();
            this.domainParameterDescriptor=
                    DefaultParameterDescriptor.create(
                            name,
                            "Additional " + identifier + " domain",
                            List.class,
                            null,
                            false);
        }
        @Override
        public String toString() {
            return "DomainDescriptor [identifier=" + identifier + ", propertyName=" + propertyName + ", dataType=" + dataType
                    + ", additionalPropertyName=" + (additionalPropertyName != null ? additionalPropertyName : "__UNAVAILABLE__") "]";
        }

        /**
         * Extract the time domain extrema.
         *
         * @param extrema a {@link String} either TIME_DOMAIN_MAXIMUM or TIME_DOMAIN_MINIMUM.
         *
         * @return either TIME_DOMAIN_MAXIMUM or TIME_DOMAIN_MINIMUM as a {@link String}.
         * TODO use num for extrema
         */
        private String getExtrema(String extrema) {
            try {
                String attribute = propertyName;
                // In case the domain has range, we will check the second element
                // in case we are looking for the maximum
                if (domainType != DomainType.SINGLE_VALUE && extrema.toLowerCase().endsWith("maximum")) {
                        attribute = additionalPropertyName;
                }
                final FeatureCalc visitor = createExtremaQuery(extrema, attribute);
               
                // check result
                CalcResult tempRes = visitor.getResult();
                if (tempRes == null){
                    throw new IllegalStateException("Unable to compute extrema value:"+extrema);
                }
                final Object result=tempRes.getValue();
                if (result == null){
                    throw new IllegalStateException("Unable to compute extrema value:"+extrema);
                }               
                return ConvertersHack.convert(result, String.class);
            } catch (IOException e) {
                if(LOGGER.isLoggable(Level.WARNING))
                        LOGGER.log(Level.WARNING,"Unable to compute extrema for TIME_DOMAIN",e);
                return null;
            }
        }
       
        /**
         * Retrieves the values for this domain
         * @return
         */
        private String getValues() {
            if (domainType == DomainType.SINGLE_VALUE) {
                return getSingleValues();
            }
            return getRangeValues();
        }

        /**
         * Retrieves the Range values for this domain
         * @return
         */
        private String getRangeValues() {
            try {
                Set<String> result = extractDomain(propertyName, additionalPropertyName, domainType);
                if (result.size() <= 0){
                    return "";
                }
               
                final StringBuilder buff= new StringBuilder();
                for(Iterator it = result.iterator(); it.hasNext();){
                    buff.append(ConvertersHack.convert(it.next(), String.class));
                    if (it.hasNext()) {
                        buff.append(",");
                    }
                }
                return buff.toString();
            } catch (IOException e) {
                if(LOGGER.isLoggable(Level.WARNING))
                    LOGGER.log(Level.WARNING,"Unable to parse attribute: " + identifier ,e);
            return "";
            }
        }

        /**
         * Retrieves the single values list of this domain (no ranges available)
         * @return
         */
        private String getSingleValues(){
            try {
               
                // implicit ordering
                final Set result = new TreeSet(extractDomain(propertyName));         
                // check result
                if (result.size() <= 0){
                    return "";
                }
               
                final StringBuilder buff= new StringBuilder();
                for(Iterator it = result.iterator(); it.hasNext();){
                    buff.append(ConvertersHack.convert(it.next(), String.class));
                    if (it.hasNext()) {
                        buff.append(",");
                    }
                }
                return buff.toString();
            } catch (IOException e) {
                if(LOGGER.isLoggable(Level.WARNING))
                        LOGGER.log(Level.WARNING,"Unable to parse attribute: " + identifier ,e);
                return "";
            }
        }

        /**
         * This method is responsible for creating {@link Filter} that encompasses the
         * provided {@link List} of values for this {@link DomainManager}.
         *
         * @param values the {@link List} of values to use for building the containment {@link Filter}.
         * @return a {@link Filter} that encompasses the
         * provided {@link List} of values for this {@link DomainManager}.
         */
        private Filter createFilter(List values) {
           
            // === create the filter
            // loop values and AND them
            final int size = values.size();
            final List<Filter> filters = new ArrayList<Filter>();
            FilterFactory2 ff = FeatureUtilities.DEFAULT_FILTER_FACTORY;
            for (int i = 0; i < size; i++) {
                // checks
                Object value = values.get(i);
                if (value == null) {
                    if (LOGGER.isLoggable(Level.INFO)) {
                        LOGGER.info("Ignoring null date for the filter:" + this.identifier);
                    }
                    continue;
                }
                if (domainType == DomainType.SINGLE_VALUE) {
                    // Domain made of single values
                    if(value instanceof Range){
                        // RANGE                       
                        final Range range= (Range)value;
                        filters.add(
                                ff.and(
                                        ff.lessOrEqual(
                                                ff.property(propertyName),
                                                ff.literal(range.getMaxValue())),
                                        ff.greaterOrEqual(
                                                ff.property(propertyName),
                                                ff.literal(range.getMinValue()))
                                ));
                    else {
                        // SINGLE value
                        filters.add(
                                ff.equal(
                                        ff.property(propertyName),
                                        ff.literal(value),true)
                                    );
                    }
                } else { //domainType == DomainType.RANGE
                    // Domain made of ranges such as (beginTime,endTime) , (beginElevation,endElevation) , ...
                    if(value instanceof Range){
                        // RANGE                       
                        final Range range= (Range)value;
                        final Comparable maxValue = range.getMaxValue();
                        final Comparable minValue = range.getMinValue();
                        if(maxValue.compareTo(minValue)!=0){
                            // logic comes from Range.intersectsNC(Range)
                            // in summary, requestedMax > min && requestedMin < max
                            Filter maxCondition = ff.greaterOrEqual(
                                                        ff.literal(maxValue),
                                                        ff.property(propertyName));
                            Filter minCondition = ff.lessOrEqual(
                                                        ff.literal(minValue),
                                                        ff.property(additionalPropertyName));
                           
                            filters.add(ff.and(Arrays.asList(maxCondition,minCondition)));
                            continue;
                        } else {
                            value=maxValue;
                        }
                    }
                    filters.add(
                            ff.and(
                                    ff.lessOrEqual(
                                            ff.property(propertyName),
                                            ff.literal(value)),
                                    ff.greaterOrEqual(
                                            ff.property(additionalPropertyName),
                                            ff.literal(value))));
                }
            }
            return ff.or(filters);
        }
    }

    /**
     * An {@link DomainManager} class which allows to deal with additional domains
     * (if any) defined inside the mosaic. It provides DOMAIN_ALIAS <--to--> original attribute mapping
     * capabilities, metadata retrieval, filter creation, and domain support check
     *
     * @author Daniele Romagnoli, GeoSolutions SAS.
     */
    class DomainManager {

        private final Map<String, DomainDescriptor> domainsMap = new HashMap<String, DomainDescriptor>();
       
        private final List<DimensionDescriptor> dimensions = new ArrayList<DimensionDescriptor>();

        private final boolean attributeHasRange(String attribute) {
            return attribute.contains(Utils.RANGE_SPLITTER_CHAR);
        }

        DomainManager(Map<String, String> additionalDomainAttributes,
                SimpleFeatureType simpleFeatureType) {
            Utilities.ensureNonNull("additionalDomainAttributes", additionalDomainAttributes);
            Utilities.ensureNonNull("simpleFeatureType", simpleFeatureType);
            init(additionalDomainAttributes, simpleFeatureType);
        }

        /**
         * @param domainAttributes
         * @param simpleFeatureType
         * @throws IllegalArgumentException
         */
        private void init(Map<String, String> domainAttributes, SimpleFeatureType simpleFeatureType)
                throws IllegalArgumentException {
            for (java.util.Map.Entry<String, String> entry : domainAttributes.entrySet()) {

                DomainType domainType = DomainType.SINGLE_VALUE;
                final String domainName = entry.getKey();
                String propertyName = entry.getValue();
                // is the name equals to the propertyname?
                try {

                    // Domain with ranges management
                    if (attributeHasRange(propertyName)) {
                        domainType = domainAttributes.containsKey(Utils.TIME_DOMAIN) ? DomainType.TIME_RANGE
                                : DomainType.NUMBER_RANGE;
                        addDomain(domainName, propertyName, domainType, simpleFeatureType);
                        continue;
                    } else {
                        propertyName = extractAttributes(propertyName);
                        if (simpleFeatureType.getDescriptor(propertyName) != null) {
                            // add
                            addDomain(domainName, propertyName, domainType, simpleFeatureType);
                            // continue
                            continue;
                        }
                    }

                } catch (Exception e) {
                    if (LOGGER.isLoggable(Level.FINE)) {
                        LOGGER.log(Level.FINE, e.getLocalizedMessage(), e);
                    }
                }

                // ok why we don't have it? Maybe shapefile name truncation?
                if (propertyName.length() > 10) {
                    // hakc for shapes
                    propertyName = propertyName.substring(0, 10);
                    // alias in provided type
                   
                    try {
                        if ( simpleFeatureType.getDescriptor(propertyName) != null) {
                            // add
                            addDomain(domainName, propertyName, domainType, simpleFeatureType);

                            // continue
                            continue;
                        }

                    } catch (Exception e) {
                        if (LOGGER.isLoggable(Level.FINE)) {
                            LOGGER.log(Level.FINE, e.getLocalizedMessage(), e);
                        }
                    }
                }

                // if I got here, we are in trouble. No way to add this param
                throw new IllegalArgumentException("Unable to add this domain:" + domainName + "-"
                        + propertyName);

            }
        }

        /**
         * build an AdditionalDomainManager on top of the provided additionalDomainAttributes (a comma separated list of attribute names).
         *
         * @param additionalDomainAttributes
         * @param simpleFeatureType
         */
        DomainManager(String additionalDomainAttributes, SimpleFeatureType simpleFeatureType) {
            Utilities.ensureNonNull("additionalDomainAttributes", additionalDomainAttributes);
            Utilities.ensureNonNull("simpleFeatureType", simpleFeatureType);

            final Map<String, String> domainPairs = new HashMap<String, String>();

            // split, looking for multiple values
            final String[] additionalDomainsNames = additionalDomainAttributes.split(",");
            if (additionalDomainsNames.length <= 0) {
                throw new IllegalArgumentException("Number of Domains should be > 0");
            }

            // add al the provided domain
            for (String propertyName : additionalDomainsNames) {
                String domainName = cleanupDomainName(propertyName);
                domainPairs.put(domainName, propertyName);
            }
            init(domainPairs, simpleFeatureType);
        }

        /**
         *
         * @param domainName
         * @return
         *
         * @TODO We can surely improve it by making use of Regular Expressions
         */
        private String cleanupDomainName(String domainName) {
            if (attributeHasRange(domainName) || ( domainName.contains("(")
                    && domainName.contains(")"))) {
                // Getting rid of the attributes definition to get only the domain name
                domainName = domainName.substring(0, domainName.indexOf("("));
            }
            return domainName;
        }

        /**
         * Add a domain to the manager
         *
         * @param domain the name of the domain
         * @param propertyName
         * @param featureType
         */
        private void addDomain(String name, String propertyName, final DomainType domainType, final SimpleFeatureType featureType) {
            Utilities.ensureNonNull("name", name);
            Utilities.ensureNonNull("propertyName", propertyName);

            // === checks
            // existing!
            if (domainsMap.containsKey(name)) {
                throw new IllegalArgumentException("Trying to add a domain with an existing name"
                        + name);
            }

            // === checks
            // has Ranges
            String basePropertyName = propertyName;
            String additionalPropertyName = null;
            if (domainType != DomainType.SINGLE_VALUE) {

                // Deal with a case like this: time(begin,endtime)
                propertyName = extractAttributes(propertyName);

                // Getting 2 attributes for this domain
                String properties[] = propertyName.split(Utils.RANGE_SPLITTER_CHAR);
                if (properties == null || properties.length != 2) {
                    throw new IllegalArgumentException(
                            "Malformed domain with ranges: it should contain 2 attributes");
                }

                basePropertyName = properties[0];
                additionalPropertyName = properties[1];
            }

            // ad with uppercase and with suffix, the parameter that describes it will match this
            final String upperCase = name.toUpperCase();
            final AttributeDescriptor descriptor = featureType.getDescriptor(basePropertyName);
            final String type = descriptor.getType().getBinding().getName();
            domainsMap.put(upperCase + DomainDescriptor.DOMAIN_SUFFIX, new DomainDescriptor(name,
                    domainType, type, basePropertyName, additionalPropertyName));
            addDimensionDescriptor(name, upperCase, basePropertyName, additionalPropertyName);
        }

        private void addDimensionDescriptor(String name, String upperCase, String basePropertyName, String additionalPropertyName) {
            final String unitsName = upperCase.equalsIgnoreCase(Utils.TIME_DOMAIN) ? CoverageUtilities.UCUM.TIME_UNITS.getName()
                    : upperCase.equalsIgnoreCase(Utils.ELEVATION_DOMAIN) ? CoverageUtilities.UCUM.ELEVATION_UNITS.getName() :
                        "FIXME"; //TODO: ADD UCUM units Management
            final String unitsSymbol = upperCase.equalsIgnoreCase(Utils.TIME_DOMAIN) ? CoverageUtilities.UCUM.TIME_UNITS.getSymbol()
                    : upperCase.equalsIgnoreCase(Utils.ELEVATION_DOMAIN) ? CoverageUtilities.UCUM.ELEVATION_UNITS.getSymbol() :
                        "FIXME"; //TODO: ADD UCUM units Management
            final DimensionDescriptor dimensionDescriptor = new DefaultDimensionDescriptor(name, unitsName, unitsSymbol, basePropertyName, additionalPropertyName);
            dimensions.add(dimensionDescriptor);
        }

        private String extractAttributes(String propertyName) {
            if (propertyName.contains("(") && propertyName.contains(")")) {
                // extract the ranges attributes
                propertyName = propertyName.substring(propertyName.indexOf("("))
                        .replace("(", "").replace(")", "");
            }
            return propertyName;
        }

        /**
         * Check whether a specific parameter (identified by the {@link Identifier} name) is supported by this manager (and therefore, by the reader).
         *
         * @param name
         * @return
         */
        public boolean isParameterSupported(final Identifier name) {
            if (!domainsMap.isEmpty()) {
                for (DomainDescriptor domain : domainsMap.values()) {
                    final ReferenceIdentifier nameLoc = domain.getDomainaParameterDescriptor()
                            .getName();
                    if (nameLoc.equals(name)) {
                        return true;
                    }
                }
            }
            return false;
        }

        /**
         * Setup the List of metadataNames for this additional domains manager
         *
         * @return
         */
        public List<String> getMetadataNames() {
            final List<String> metadataNames = new ArrayList<String>();
            if (!domainsMap.isEmpty()) {
                for (DomainDescriptor domain : domainsMap.values()) {
                    String domainName = domain.getIdentifier().toUpperCase();
                    metadataNames.add(domainName + DomainDescriptor.DOMAIN_SUFFIX);
                    if (domain.getDataType() != null) {
                        metadataNames.add(domainName + DomainDescriptor.DOMAIN_SUFFIX + DomainDescriptor.DATATYPE_SUFFIX);
                    }
                    metadataNames.add(DomainDescriptor.HAS_PREFIX + domainName + DomainDescriptor.DOMAIN_SUFFIX);
                }
            }
            return metadataNames;
        }

        /**
         * Return the value of a specific metadata by parsing the requested name as a Domain Name
         *
         * @param name
         * @return
         */
        public String getMetadataValue(String name) {
            Utilities.ensureNonNull("name", name);

            String value = null;
            if (domainsMap.size() > 0) {
                // is a domain?
                if (domainsMap.containsKey(name)) {
                    final DomainDescriptor domainDescriptor = domainsMap.get(name);
                    value = domainDescriptor.getValues();
                } else {
                    // is a simple Has domain query?
                    if (name.startsWith(DomainDescriptor.HAS_PREFIX)) {
                        final String substring = name.substring(
                                DomainDescriptor.HAS_PREFIX.length(), name.length());
                        if (domainsMap.containsKey(substring)) {
                            return Boolean.toString(Boolean.TRUE);
                        } else {
                            return Boolean.toString(Boolean.FALSE);
                        }
                    } else if (name.endsWith(DomainDescriptor.DATATYPE_SUFFIX)) {
                        return domainsMap.get(name.substring(0, name.lastIndexOf(DomainDescriptor.DATATYPE_SUFFIX))).getDataType();
                    } else {
                        // MINUM or MAXIMUM
                        if (name.endsWith("MINIMUM") || name.endsWith("MAXIMUM")) {
                            return domainsMap.get(name.substring(0, name.lastIndexOf("_")))
                                    .getExtrema(name);
                        }
                    }
                }
            }
            return value;
        }

        /**
         * Setup a Filter on top of the specified domainRequest which is in the form "key=value"
         *
         * @param domain
         * @param values
         * @return
         */
        public Filter createFilter(String domain, List values) {
            // === checks
            if (domain == null || domain.isEmpty()) {
                throw new IllegalArgumentException("Null domain requested");
            }
            if (values == null || values.isEmpty()) {
                throw new IllegalArgumentException("Null domain values provided");
            }
            if (domainsMap.isEmpty() || !domainsMap.containsKey(domain)) {
                throw new IllegalArgumentException(
                        "requested domain is not supported by this mosaic: " + domain);
            }

            // get the property name
            DomainDescriptor domainDescriptor = domainsMap.get(domain);
            return domainDescriptor.createFilter(values);
        }

        /**
         * Return the set of dynamic parameterDescriptors (the ones related to domains) for this reader
         *
         * @return
         */
        public Set<ParameterDescriptor<List>> getDynamicParameters() {
            Set<ParameterDescriptor<List>> dynamicParameters = new HashSet<ParameterDescriptor<List>>();
            if (!domainsMap.isEmpty()) {
                for (DomainDescriptor domain : domainsMap.values()) {
                    dynamicParameters.add(domain.getDomainaParameterDescriptor());
                }
            }
            // return
            return dynamicParameters;
        }

    }

    enum DomainType {
        SINGLE_VALUE, TIME_RANGE, NUMBER_RANGE
    }
   
    /** Default {@link ColorModel}. */
    ColorModel defaultCM;

    /** Default {@link SampleModel}. */
    SampleModel defaultSM;

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

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

    OverviewsController overviewsController;

    OverviewPolicy overviewPolicy;

    DecimationPolicy decimationPolicy;

    private PathType pathType;

    boolean expandMe;

    boolean heterogeneousGranules;

    double[][] levels;

    SpatialDomainManager spatialDomainManager;

    ImageLayout defaultImageLayout;

    /** The inner {@link DomainManager} instance which allows to manage custom dimensions */
    DomainManager domainsManager;

    DomainManager elevationDomainManager;

    DomainManager timeDomainManager;
   
    volatile boolean enableEvents=false;//start disabled

    List<DimensionDescriptor> dimensionDescriptors = new ArrayList<DimensionDescriptor>();

    ImageMosaicReader parentReader;

    GranuleCatalog granuleCatalog;
   
    GranuleStore granuleStore;
   
    GranuleSource granuleSource;

    String typeName;
   
    Envelope imposedEnvelope;
   
    MosaicConfigurationBean configuration;

    public RasterManager(final ImageMosaicReader parentReader, MosaicConfigurationBean configuration)
            throws IOException {

        Utilities.ensureNonNull("ImageMosaicReader", parentReader);

        this.parentReader = parentReader;
        this.expandMe = parentReader.expandMe;
        boolean checkAuxiliaryMetadata = configuration.isCheckAuxiliaryMetadata();
        this.heterogeneousGranules = configuration.getCatalogConfigurationBean().isHeterogeneous();
        this.configuration = configuration;
        hints = parentReader.getHints();
        checkAuxiliaryFile(hints, configuration, parentReader);

        if (checkAuxiliaryMetadata) {
            hints.add(new RenderingHints(Utils.CHECK_AUXILIARY_METADATA, checkAuxiliaryMetadata));
        }

        // take ownership of the index : TODO: REMOVE THAT ONCE DEALING WITH MORE CATALOGS/RASTERMANAGERS
        // granuleCatalog = new HintedGranuleCatalog(parentReader.granuleCatalog, hints);
        granuleCatalog = parentReader.granuleCatalog;
        this.coverageFactory = parentReader.getGridCoverageFactory();
        this.coverageIdentifier = configuration != null ? configuration.getName() : ImageMosaicReader.UNSPECIFIED;
        this.pathType = configuration.getCatalogConfigurationBean().isAbsolutePath() ? PathType.ABSOLUTE : PathType.RELATIVE;

        extractOverviewPolicy();
        extractDecimationPolicy();

        // load defaultSM and defaultCM by using the sample_image if it was provided
        loadSampleImage(configuration);

        if (configuration != null) {
            CatalogConfigurationBean catalogBean = configuration.getCatalogConfigurationBean();
            typeName = catalogBean != null ? catalogBean.getTypeName() : null;
            initDomains(configuration);
            if (defaultSM == null) {
                defaultSM = configuration.getSampleModel();
            }

            if (defaultCM == null) {
                defaultCM = configuration.getColorModel();
            }
           
            if (defaultSM != null && defaultCM != null && defaultImageLayout == null) {
                defaultImageLayout= new ImageLayout().setColorModel(defaultCM).setSampleModel(defaultSM);
            }
           
            levels = configuration.getLevels();
            final double[] highRes = levels[0];
            final int numOverviews = configuration.getLevelsNum() - 1;
            double[][] overviews = null;
            if (numOverviews > 0) {
                overviews = new double[numOverviews][2];
                for (int i = 0; i < numOverviews; i++) {
                    overviews[i][0] = levels[i+1][0];
                    overviews[i][1] = levels[i+1][1];
                }
            }
            overviewsController = new OverviewsController(highRes,
                  numOverviews, overviews);
            imposedEnvelope = configuration.getEnvelope();
        }
    }

    private void checkAuxiliaryFile(Hints hints, MosaicConfigurationBean configuration,
            ImageMosaicReader parentReader) {
        if (configuration != null && configuration.getAuxiliaryFilePath() != null) {
            hints.add(new RenderingHints(Utils.AUXILIARY_FILES_PATH, configuration.getAuxiliaryFilePath()));
            if (!configuration.getCatalogConfigurationBean().isAbsolutePath() && !hints.containsKey(Utils.PARENT_DIR)) {
                String parentDir = null;
                if (parentReader.parentDirectory != null) {
                    parentDir = parentReader.parentDirectory.getAbsolutePath();
                } else {
                    Object source = parentReader.getSource();
                    if (source != null && source instanceof File && ((File)source).isDirectory()) {
                        parentDir = ((File)source).getAbsolutePath();
                    }
                }
                hints.add(new RenderingHints(Utils.PARENT_DIR, parentDir));
            }
        }
    }

    private void initDomains(MosaicConfigurationBean configuration) throws IOException {
        checkTypeName();
        if (typeName != null) {

            final SimpleFeatureType schema = granuleCatalog.getType(typeName);
            if (schema != null) {
                // additional domain attributes
                final String additionalDomainConfig = configuration.getAdditionalDomainAttributes();
                if (additionalDomainConfig != null && domainsManager == null) {
                    domainsManager = new DomainManager(additionalDomainConfig, schema);
                    dimensionDescriptors.addAll(domainsManager.dimensions);
                }

                // time attribute
                final String timeDomain = configuration.getTimeAttribute();
                if (timeDomain != null && timeDomainManager == null) {
                    final HashMap<String, String> init = new HashMap<String, String>();
                    init.put(Utils.TIME_DOMAIN, timeDomain);
                    timeDomainManager = new DomainManager(init, schema);
                    dimensionDescriptors.addAll(timeDomainManager.dimensions);
                }

                // elevation attribute
                final String elevationAttribute = configuration.getElevationAttribute();
                if (elevationAttribute != null && elevationDomainManager == null) {
                    final HashMap<String, String> init = new HashMap<String, String>();
                    init.put(Utils.ELEVATION_DOMAIN, elevationAttribute);
                    elevationDomainManager = new DomainManager(init, schema);
                    dimensionDescriptors.addAll(elevationDomainManager.dimensions);
                }
            }
        }
    }
   
    private void checkTypeName() throws IOException {
        if (typeName == null) {
            URL sourceURL = parentReader.sourceURL;
            if (sourceURL.getPath().endsWith("shp")) {
                typeName = FilenameUtils.getBaseName(DataUtilities.urlToFile(sourceURL)
                        .getCanonicalPath());
            } else {
                typeName = configuration.getName();
            }
        }
        if (typeName == null && granuleCatalog != null) {
            String[] typeNames = granuleCatalog.getTypeNames();
            typeName = (typeNames != null && typeNames.length > 0) ? typeNames[0] : null;
        }
    }

    /**
   * This code tries to load the sample image from which we can extract SM and CM to use when answering to requests
   * that falls within a hole in the mosaic.
    * @param configuration
   */
  private void loadSampleImage(MosaicConfigurationBean configuration) {
      if (this.parentReader.sourceURL == null) {
          //TODO: I need to define the sampleImage somehow for the ImageMosaicDescriptor case
          return;
      }
   
      final URL baseURL=this.parentReader.sourceURL;
      final File baseFile= DataUtilities.urlToFile(baseURL);
      // in case we do not manage to convert the source URL we leave right awaycd sr
      if (baseFile==null){
        if(LOGGER.isLoggable(Level.FINE))
          LOGGER.fine("Unable to find sample image for path "+baseURL);
        return;
      }
      String baseName = baseFile.getParent() + "/";
      String fileName = null;
      File sampleImageFile = null;
      if (configuration != null) {
          String name = configuration.getName();
          if (name != null) {
              fileName = baseName + name + Utils.SAMPLE_IMAGE_NAME;
              sampleImageFile = new File (fileName);
              if (!sampleImageFile.exists() || !sampleImageFile.canRead()) {
                  sampleImageFile = null;
              }
          }
      }
     
      if (sampleImageFile == null) {
          sampleImageFile = new File(baseName + Utils.SAMPLE_IMAGE_NAME);
      }
      final RenderedImage sampleImage = Utils.loadSampleImage(sampleImageFile);
      if(sampleImage!=null){
       
        // load SM and CM
        defaultCM= sampleImage.getColorModel();
        defaultSM= sampleImage.getSampleModel();
       
        // default ImageLayout
        defaultImageLayout= new ImageLayout().setColorModel(defaultCM).setSampleModel(defaultSM);
      }
      else
        if(LOGGER.isLoggable(Level.WARNING))
          LOGGER.warning("Unable to find sample image for path "+baseURL);
  }

  /**
   * This method is responsible for checking the overview policy as defined by
   * the provided {@link Hints}.
   *
   * @return the overview policy which can be one of
   *         {@link OverviewPolicy#IGNORE},
   *         {@link OverviewPolicy#NEAREST},
   *         {@link OverviewPolicy#SPEED}, {@link OverviewPolicy#QUALITY}.
   *         Default is {@link OverviewPolicy#NEAREST}.
   */
  private OverviewPolicy extractOverviewPolicy() {
   
    // check if a policy was provided using hints (check even the
    // deprecated one)
    if (this.hints != null)
      if (this.hints.containsKey(Hints.OVERVIEW_POLICY))
        overviewPolicy = (OverviewPolicy) this.hints.get(Hints.OVERVIEW_POLICY);
 
    // use default if not provided. Default is nearest
    if (overviewPolicy == null) {
      overviewPolicy = OverviewPolicy.getDefaultPolicy();
    }
    assert overviewPolicy != null;
    return overviewPolicy;
  }
 
  /**
         * This method is responsible for checking the decimation policy as defined by
         * the provided {@link Hints}.
         *
         * @return the decimation policy which can be one of
         *         {@link DecimationPolicy#ALLOW},
         *         {@link DecimationPolicy#DISALLOW}.
         *         Default is {@link DecimationPolicy#ALLOW}.
         */
  private DecimationPolicy extractDecimationPolicy() {
            if (this.hints != null)
                if (this.hints.containsKey(Hints.DECIMATION_POLICY))
                    decimationPolicy = (DecimationPolicy) this.hints.get(Hints.DECIMATION_POLICY);
   
            // use default if not provided. Default is allow
            if (decimationPolicy == null) {
                decimationPolicy = DecimationPolicy.getDefaultPolicy();
            }
            assert decimationPolicy != null;
            return decimationPolicy;

        }

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

    // create a request
    final RasterLayerRequest request= new RasterLayerRequest(params,this);
    if (request.isEmpty()){
      if(LOGGER.isLoggable(Level.FINE))
        LOGGER.log(Level.FINE,"Request is empty: "+ request.toString());
      return Collections.emptyList();   
    }
   
    // create a response for the provided request
    final RasterLayerResponse response= new RasterLayerResponse(request,this);
   
    // execute the request
    final GridCoverage2D elem = response.createResponse();
    if (elem != null){
      return Collections.singletonList(elem);
    }
    return Collections.emptyList();
   
   
  }
 
  void getGranuleDescriptors(final Query q,final GranuleCatalogVisitor visitor)throws IOException {
    granuleCatalog.getGranuleDescriptors(q,visitor);

  }

  public PathType getPathType() {
    return pathType;
  }


  public String getCoverageIdentifier() {
    return coverageIdentifier;
  }

 
  public Hints getHints() {
    return hints;
  }

  public GridCoverageFactory getCoverageFactory() {
    return coverageFactory;
  }

    public String getTypeName() {
        return typeName;
    }

    /**
     * @param metadataName
     * @param attributeName
     * @return
     * @throws IOException
     */
    FeatureCalc createExtremaQuery(String metadataName, String attributeName) throws IOException {
        final Query query = new Query(typeName);
        query.setPropertyNames(Arrays.asList(attributeName));

        final FeatureCalc visitor=
            metadataName.toLowerCase().endsWith("maximum")?
                new MaxVisitor(attributeName):new MinVisitor(attributeName);
        granuleCatalog.computeAggregateFunction(query, visitor);
        return visitor;
    }

    /**
     * Extract the domain of a dimension as a set of unique values.
     *
     * <p>
     * It retrieves a comma separated list of values as a Set of {@link String}.
     *
     * @return a comma separated list of values as a {@link String}.
     * @throws IOException
     */
    private Set extractDomain(final String attribute) throws IOException {
        Query query = new Query(typeName);
        query.setPropertyNames(Arrays.asList(attribute));
        final UniqueVisitor visitor = new UniqueVisitor(attribute);
        granuleCatalog.computeAggregateFunction(query, visitor);
        return visitor.getUnique();
    }

    /**
     * Extract the domain of a dimension (with Range) as a set of values.
     *
     * <p>
     * It retrieves a comma separated list of values as a Set of {@link String}.
     *
     * @param domainType
     *
     * @return a comma separated list of values as a Set of {@link String}.
     * @throws IOException
     */
    private Set extractDomain(final String attribute, final String secondAttribute, final DomainType domainType)
            throws IOException {
        final Query query = new Query(typeName);
       
        final PropertyName propertyName = FeatureUtilities.DEFAULT_FILTER_FACTORY.property(attribute);
        query.setPropertyNames(Arrays.asList(attribute, secondAttribute));
       
        final SortByImpl[] sb = new SortByImpl[]{new SortByImpl(propertyName, SortOrder.ASCENDING)};
        // Checking whether it supports sorting capabilities
        if(granuleCatalog.getQueryCapabilities(typeName).supportsSorting(sb)){
            query.setSortBy(sb);
        }
       
        final FeatureCalc visitor = domainType == DomainType.TIME_RANGE ? new DateRangeVisitor(attribute, secondAttribute) : new RangeVisitor(attribute, secondAttribute);
        granuleCatalog.computeAggregateFunction(query, visitor);
        return domainType == DomainType.TIME_RANGE ? ((DateRangeVisitor)visitor).getRange() : ((RangeVisitor)visitor).getRange() ;
       
    }


        /**
         * TODO this should not leak through
         * @return
         */
    public GranuleCatalog getGranuleCatalog() {
        return granuleCatalog;
    }

    /**
     * Create a store for the coverage related to this {@link RasterManager} using the
     * provided schema
     *
     * @param indexSchema
     * @throws IOException
     */
    public void createStore (SimpleFeatureType indexSchema) throws IOException {
        final String typeName = indexSchema.getTypeName();
        final SimpleFeatureType type = typeName != null ? granuleCatalog.getType(typeName) : null;
        if (type == null) {
            granuleCatalog.createType(indexSchema);
            this.typeName = typeName;
        } else {
            if (this.typeName == null) {
                this.typeName = typeName;
            }
            // remove them all, assuming the schema has not changed
            final Query query = new Query(type.getTypeName());
            query.setFilter(Filter.INCLUDE);
            granuleCatalog.removeGranules(query);
        }
    }

    /**
     * Remove a store for the coverage related to this {@link RasterManager}
     * @param forceDelete
     *
     * @param indexSchema
     * @throws IOException
     */
    public void removeStore (String typeName, boolean forceDelete, boolean checkForReferences) throws IOException {
        Utilities.ensureNonNull("typeName", typeName);
        if (typeName != null) {
            // Preliminar granules removal...
            // Should we send a message instead reporting that the catalog
            // still contain some granules before allowing for a removal??
            final Query query = new Query(typeName);
            query.setFilter(Filter.INCLUDE);

            // cleaning up granules and underlying readers
            cleanupGranules(query, checkForReferences, forceDelete);

            // removing records from the catalog
            granuleCatalog.removeGranules(query);
            granuleCatalog.removeType(typeName);
        }
    }

    /**
     * Delete granules from query.
     * @param query
     * @param checkForReferences
     * @throws IOException
     */
    private void cleanupGranules(Query query, boolean checkForReferences, boolean deleteData) throws IOException {
        final SimpleFeatureCollection collection = granuleCatalog.getGranules(query);
        UniqueVisitor visitor = new UniqueVisitor(parentReader.locationAttributeName);
        collection.accepts(visitor, null);
        Set<String> features = visitor.getUnique();
        final String coverageName = query.getTypeName();

        for (String feature: features) {
            final URL rasterPath = pathType.resolvePath(DataUtilities.fileToURL(parentReader.parentDirectory).toString(), feature);
            boolean delete = true;
            if (checkForReferences) {
                delete = !checkForReferences(coverageName);
               
            }
            AbstractGridFormat format = (AbstractGridFormat) GridFormatFinder.findFormat(rasterPath, excludeMosaicHints);
            if (format != null) {
                GridCoverage2DReader coverageReader = null;
                try {
                    coverageReader = (GridCoverage2DReader) format.getReader(rasterPath, hints);
                    if (coverageReader instanceof StructuredGridCoverage2DReader) {
                        StructuredGridCoverage2DReader reader = (StructuredGridCoverage2DReader) coverageReader;
                        if (delete) {
                            reader.delete(deleteData);
                        } else {
                            reader.removeCoverage(coverageName, false);
                        }
                    } else if (deleteData) {
                        final boolean removed = FileUtils.deleteQuietly(DataUtilities.urlToFile(rasterPath));
                    }
                } finally {
                    if (coverageReader != null) {
                        try {
                            coverageReader.dispose();
                        } catch (Throwable t) {
                            //Ignoring exceptions on disposing readers
                        }
                    }
                }
            }
        }
    }

    /**
     * Check if there is any granule referred by other coverages.
     * @param coverageName
     * @return
     * @throws IOException
     */
    private boolean checkForReferences(String coverageName) throws IOException {
        final String[] coverageNames = parentReader.getGridCoverageNames();
        for (String typeName : coverageNames) {
            if (!coverageName.equalsIgnoreCase(typeName)) {
                Query query = new Query(typeName);
                final SimpleFeatureCollection collection = granuleCatalog.getGranules(query);
                UniqueVisitor visitor = new UniqueVisitor(parentReader.locationAttributeName);
                collection.accepts(visitor, null);
                Set<String> features = visitor.getUnique();
                if (features.size() > 0) {
                    return true;
                }
            }
        }
        return false;
    }

    public GranuleSource getGranuleSource(final boolean readOnly, final Hints hints) {
        synchronized (this) {
            if (readOnly) {
                if (granuleSource == null) {
                    granuleSource = new GranuleCatalogSource(granuleCatalog, typeName, hints);
                }
                return granuleSource;
            } else {
                if (granuleStore == null) {
                    granuleStore = new GranuleCatalogStore(granuleCatalog, typeName, hints);
                }
                return granuleStore;
            }
        }
    }

    public List<DimensionDescriptor> getDimensionDescriptors() {
        return dimensionDescriptors;
    }

    public MosaicConfigurationBean getConfiguration() {
        return configuration;
    }

    public void setConfiguration(MosaicConfigurationBean configuration) {
        this.configuration = configuration;
    }

    public void dispose() {
        synchronized (this) {
            try {
                if (granuleCatalog != null) {
                    this.granuleCatalog.dispose();
                }
            } catch (Exception e) {
                if (LOGGER.isLoggable(Level.FINE))
                    LOGGER.log(Level.FINE, e.getLocalizedMessage(), e);
            } finally {
                if (granuleSource != null) {
                    granuleSource = null;
                }
                if (granuleStore != null) {
                    granuleStore = null;
                }
                if (granuleCatalog != null) {
                    granuleCatalog = null;
                }
            }
        }
    }

    void initialize(final boolean checkDomains) throws IOException {
        final BoundingBox bounds = granuleCatalog.getBounds(typeName);
        if (checkDomains) {
            initDomains(configuration);
        }

        if (bounds.isEmpty()) {
            throw new IllegalArgumentException("Cannot create a mosaic out of an empty index");
        }

        // we might have an imposed bbox
        CoordinateReferenceSystem crs = bounds.getCoordinateReferenceSystem();
        GeneralEnvelope originalEnvelope = null;

        if (imposedEnvelope == null) {
            originalEnvelope = new GeneralEnvelope(bounds);
        } else {
            originalEnvelope = new GeneralEnvelope(imposedEnvelope);
            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
        OverviewLevel highResOvLevel = overviewsController.resolutionsLevels.get(0);
        final double highestRes[] = new double[] { highResOvLevel.resolutionX, highResOvLevel.resolutionY };
        GridEnvelope2D originalGridRange = new GridEnvelope2D(new Rectangle(
                (int) (originalEnvelope.getSpan(0) / highestRes[0]),
                (int) (originalEnvelope.getSpan(1) / highestRes[1])));
        AffineTransform2D 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]);

        try {
            spatialDomainManager = new SpatialDomainManager(originalEnvelope,
                    (GridEnvelope2D) originalGridRange, crs, raster2Model, overviewsController);
        } catch (TransformException e) {
            throw new IOException("Exception occurred while initializing the SpatialDomainManager", e);
        } catch (FactoryException e) {
            throw new IOException("Exception occurred while initializing the SpatialDomainManager", e);
        }
    }

    /**
     * Return the metadataNames for this manager
     *
     * @return
     */
    String[] getMetadataNames() {
        final List<String> metadataNames = new ArrayList<String>();
        metadataNames.add(GridCoverage2DReader.TIME_DOMAIN);
        metadataNames.add(GridCoverage2DReader.HAS_TIME_DOMAIN);
        metadataNames.add(GridCoverage2DReader.TIME_DOMAIN_MINIMUM);
        metadataNames.add(GridCoverage2DReader.TIME_DOMAIN_MAXIMUM);
        metadataNames.add(GridCoverage2DReader.TIME_DOMAIN_RESOLUTION);
        metadataNames.add(GridCoverage2DReader.TIME_DOMAIN + DomainDescriptor.DATATYPE_SUFFIX);

        metadataNames.add(GridCoverage2DReader.ELEVATION_DOMAIN);
        metadataNames.add(GridCoverage2DReader.ELEVATION_DOMAIN_MINIMUM);
        metadataNames.add(GridCoverage2DReader.ELEVATION_DOMAIN_MAXIMUM);
        metadataNames.add(GridCoverage2DReader.HAS_ELEVATION_DOMAIN);
        metadataNames.add(GridCoverage2DReader.ELEVATION_DOMAIN_RESOLUTION);
        metadataNames.add(GridCoverage2DReader.ELEVATION_DOMAIN + DomainDescriptor.DATATYPE_SUFFIX);

        if (domainsManager != null) {
            metadataNames.addAll(domainsManager.getMetadataNames());
        }
        return metadataNames.toArray(new String[metadataNames.size()]);
    }

    /**
     * Return the metadata value for the specified metadata name
     * @param name the name of the metadata to be returned
     * @return
     */
    String getMetadataValue(String name) {
        String value = null;
        final boolean hasTimeDomain = timeDomainManager != null;
        final boolean hasElevationDomain = elevationDomainManager != null;

        if (name.equalsIgnoreCase(GridCoverage2DReader.HAS_ELEVATION_DOMAIN))
            return String.valueOf(hasElevationDomain);

        if (name.equalsIgnoreCase(GridCoverage2DReader.HAS_TIME_DOMAIN)) {
            return String.valueOf(hasTimeDomain);
        }

        // NOT supported
        if (name.equalsIgnoreCase(GridCoverage2DReader.TIME_DOMAIN_RESOLUTION)) {
            return null;
        }
        // NOT supported
        if (name.equalsIgnoreCase(GridCoverage2DReader.ELEVATION_DOMAIN_RESOLUTION)) {
            return null;
        }

        if (hasTimeDomain) {
            if (name.equalsIgnoreCase("time_domain")) {
                return timeDomainManager.getMetadataValue(name);
            }
            if ((name.equalsIgnoreCase("time_domain_minimum") || name
                    .equalsIgnoreCase("time_domain_maximum"))) {
                return timeDomainManager.getMetadataValue(name);
            }
            if (name.equalsIgnoreCase("time_domain_datatype")) {
                return timeDomainManager.getMetadataValue(name);
            }
        }

        if (hasElevationDomain) {
            if (name.equalsIgnoreCase("elevation_domain")) {
                return elevationDomainManager.getMetadataValue(name);
            }

            if (name.equalsIgnoreCase("elevation_domain_minimum")
                    || name.equalsIgnoreCase("elevation_domain_maximum")) {
                return elevationDomainManager.getMetadataValue(name);
            }
            if (name.equalsIgnoreCase("elevation_domain_datatype")) {
                return elevationDomainManager.getMetadataValue(name);
            }
        }

        // check additional domains
        if (domainsManager != null) {
            return domainsManager.getMetadataValue(name);
        }

        //
        return value;
    }
}
TOP

Related Classes of org.geotools.gce.imagemosaic.RasterManager$DomainDescriptor

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.