Package org.geotools.gce.imagemosaic

Source Code of org.geotools.gce.imagemosaic.Utils

/*
*    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 it.geosolutions.imageio.pam.PAMDataset;
import it.geosolutions.imageio.pam.PAMDataset.PAMRasterBand;
import it.geosolutions.imageio.pam.PAMDataset.PAMRasterBand.Metadata;
import it.geosolutions.imageio.pam.PAMDataset.PAMRasterBand.Metadata.MDI;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Queue;
import java.util.Set;
import java.util.TimeZone;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageInputStreamSpi;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import javax.media.jai.BorderExtender;
import javax.media.jai.Histogram;
import javax.media.jai.ImageLayout;
import javax.media.jai.JAI;
import javax.media.jai.RasterFactory;
import javax.media.jai.TileCache;
import javax.media.jai.TileScheduler;
import javax.media.jai.remote.SerializableRenderedImage;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.geotools.data.DataAccessFactory.Param;
import org.geotools.data.DataStoreFactorySpi;
import org.geotools.data.DataUtilities;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.Hints;
import org.geotools.factory.Hints.Key;
import org.geotools.filter.visitor.DefaultFilterVisitor;
import org.geotools.gce.imagemosaic.catalog.CatalogConfigurationBean;
import org.geotools.gce.imagemosaic.catalog.index.Indexer;
import org.geotools.gce.imagemosaic.catalog.index.IndexerUtils;
import org.geotools.gce.imagemosaic.catalog.index.ObjectFactory;
import org.geotools.gce.imagemosaic.catalog.index.ParametersType.Parameter;
import org.geotools.gce.imagemosaic.catalogbuilder.CatalogBuilderConfiguration;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.image.io.ImageIOExt;
import org.geotools.referencing.operation.matrix.XAffineTransform;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.geotools.util.Converters;
import org.geotools.util.Range;
import org.geotools.util.Utilities;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.spatial.BBOX;

import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;

/**
* Sparse utilities for the various mosaic classes. I use them to extract
* complex code from other places.
*
* @author Simone Giannecchini, GeoSolutions S.A.S.
*
*
*
* @source $URL$
*/
public class Utils {
   
    public final static FilterFactory2 FF = CommonFactoryFinder.getFilterFactory2();
   
    final private static String DATABASE_KEY = "database";

    final private static String MVCC_KEY = "MVCC";

    final private static double RESOLUTION_TOLERANCE_FACTOR = 1E-2;

    public final static Key EXCLUDE_MOSAIC = new Key(Boolean.class);

    public final static Key CHECK_AUXILIARY_METADATA = new Key(Boolean.class);

    public final static Key AUXILIARY_FILES_PATH = new Key(String.class);

    public final static Key PARENT_DIR = new Key(String.class);

    public final static Key MOSAIC_READER = new Key(ImageMosaicReader.class);

    public static final String RANGE_SPLITTER_CHAR = ";";

    public final static String INDEXER_PROPERTIES = "indexer.properties";

    public final static String INDEXER_XML = "indexer.xml";

    private static JAXBContext CONTEXT = null;
   
    public final static String PAM_DATASET = "PamDataset";

    static final String DEFAULT = "default";

    /** EHCache instance to cache histograms */
    private static Cache ehcache;

    /** RGB to GRAY coefficients (for Luminance computation) */
    public final static double RGB_TO_GRAY_MATRIX [][]= {{ 0.114, 0.587, 0.299, 0 }};

    /**
     * Flag indicating whether to compute optimized crop ops (instead of standard
     * mosaicking op) when possible (As an instance when mosaicking a single granule)
     */
    final static boolean OPTIMIZE_CROP;

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

    static {
        final String prop = System.getProperty("org.geotools.imagemosaic.optimizecrop");
        if (prop != null && prop.equalsIgnoreCase("FALSE")){
            OPTIMIZE_CROP = false;
        } else {
            OPTIMIZE_CROP = true;
        }

        try {
            CONTEXT = JAXBContext.newInstance("org.geotools.gce.imagemosaic.catalog.index");
        } catch (JAXBException e) {
            LOGGER.log(Level.FINER, e.getMessage(), e);
        }
        CLEANUP_FILTER = initCleanUpFilter();
    }
   
    public static class Prop {
        public final static String LOCATION_ATTRIBUTE = "LocationAttribute";
        public final static String ENVELOPE2D = "Envelope2D";
        public final static String LEVELS_NUM = "LevelsNum";
        public final static String LEVELS = "Levels";
        public final static String SUGGESTED_SPI = "SuggestedSPI";
        public final static String EXP_RGB = "ExpandToRGB";
        public final static String ABSOLUTE_PATH = "AbsolutePath";
        public final static String AUXILIARY_FILE = "AuxiliaryFile";
        public final static String NAME = "Name";
        public final static String INDEX_NAME = "Name";
        public final static String FOOTPRINT_MANAGEMENT = "FootprintManagement";
        public final static String HETEROGENEOUS = "Heterogeneous";
        public static final String TIME_ATTRIBUTE = "TimeAttribute";
        public static final String ELEVATION_ATTRIBUTE = "ElevationAttribute";
        public static final String ADDITIONAL_DOMAIN_ATTRIBUTES = "AdditionalDomainAttributes";

        /**
         * Sets if the target schema should be used to locate granules (default is FALSE)<br/>
         * {@value TRUE|FALSE}
         */
        public final static String USE_EXISTING_SCHEMA = "UseExistingSchema";
        public final static String TYPENAME = "TypeName";
        public final static String PATH_TYPE = "PathType";
        public final static String PARENT_LOCATION = "ParentLocation";
        public final static String ROOT_MOSAIC_DIR = "RootMosaicDirectory";
        public final static String INDEXING_DIRECTORIES = "IndexingDirectories";
        public final static String HARVEST_DIRECTORY = "HarvestingDirectory";
        public final static String CAN_BE_EMPTY = "CanBeEmpty";

        /** Sets if the reader should look for auxiliary metadata PAM files */
        public static final String CHECK_AUXILIARY_METADATA = "CheckAuxiliaryMetadata";

        //Indexer Properties specific properties
        public  static final String RECURSIVE = "Recursive";
        public static final String WILDCARD = "Wildcard";
        public static final String SCHEMA = "Schema";
        public static final String RESOLUTION_LEVELS = "ResolutionLevels";
        public static final String PROPERTY_COLLECTORS = "PropertyCollectors";
        public final static String CACHING= "Caching";
    }
        /**
     * Extracts a bbox from a filter in case there is at least one.
     *
     * I am simply looking for the BBOX filter but I am sure we could
     * use other filters as well. I will leave this as a todo for the moment.
     *
     * @author Simone Giannecchini, GeoSolutions SAS.
     * @todo TODO use other spatial filters as well
     */
    public static class BBOXFilterExtractor extends DefaultFilterVisitor{
   
      public ReferencedEnvelope getBBox() {
        return bbox;
      }
      private ReferencedEnvelope bbox;
      @Override
      public Object visit(BBOX filter, Object data) {
        final ReferencedEnvelope bbox= ReferencedEnvelope.reference(filter.getBounds());
        if(this.bbox!=null){
            this.bbox=(ReferencedEnvelope) this.bbox.intersection(bbox);
        }
        else{
            this.bbox=bbox;
        }
        return super.visit(filter, data);
      }
     
    }

  /**
   * Default wildcard for creating mosaics.
   */
  public static final String DEFAULT_WILCARD = "*.*";

  /**
   * Default path behavior with respect to absolute paths.
   */
  public static final boolean DEFAULT_PATH_BEHAVIOR = false;

  /**
   * Default behavior with respect to index caching.
   */
  private static final boolean DEFAULT_CACHING_BEHAVIOR = false;
 
 
  /**
   * Creates a mosaic for the provided input parameters.
   *
   * @param location
   *            path to the directory where to gather the elements for the
   *            mosaic.
   * @param indexName
   *            name to give to this mosaic
   * @param wildcard
   *            wildcard to use for walking through files. We are using
   *            commonsIO for this task
   * @param absolutePath
   *         tells the catalogue builder to use absolute paths.
   * @param hints hints to control reader instantiations
   * @return <code>true</code> if everything is right, <code>false</code>if
   *         something bad happens, in which case the reason should be logged
   *         to the logger.
   */
  static boolean createMosaic(
      final String location,
      final String indexName,
      final String wildcard,
      final boolean absolutePath,
      final Hints hints) {

    // create a mosaic index builder and set the relevant elements
    final CatalogBuilderConfiguration configuration = new CatalogBuilderConfiguration();
    configuration.setHints(hints);// retain hints as this may contain an instance of an ImageMosaicReader
    List<Parameter> parameterList = configuration.getIndexer().getParameters().getParameter();
              
               IndexerUtils.setParam(parameterList, Prop.ABSOLUTE_PATH, Boolean.toString(absolutePath));
               IndexerUtils.setParam(parameterList, Prop.ROOT_MOSAIC_DIR, location);
               IndexerUtils.setParam(parameterList, Prop.INDEX_NAME, indexName);
               IndexerUtils.setParam(parameterList, Prop.WILDCARD, wildcard);
               IndexerUtils.setParam(parameterList, Prop.INDEXING_DIRECTORIES, location);

        // create the builder
        // final ImageMosaicWalker catalogBuilder = new ImageMosaicWalker(configuration);
        final ImageMosaicEventHandlers eventHandler = new ImageMosaicEventHandlers();
        final ImageMosaicConfigHandler catalogHandler = new ImageMosaicConfigHandler(configuration,
                eventHandler);
        final ImageMosaicWalker walker;
        if (catalogHandler.isUseExistingSchema()) {
            walker = new ImageMosaicDatastoreWalker(catalogHandler, eventHandler);
        } else {
            walker = new ImageMosaicDirectoryWalker(catalogHandler, eventHandler);
        }

    // this is going to help us with catching exceptions and logging them
    final Queue<Throwable> exceptions = new LinkedList<Throwable>();
    try {

      final ImageMosaicEventHandlers.ProcessingEventListener listener = new ImageMosaicEventHandlers.ProcessingEventListener() {

        @Override
        public void exceptionOccurred(ImageMosaicEventHandlers.ExceptionEvent event) {
          final Throwable t = event.getException();
          exceptions.add(t);
          if (LOGGER.isLoggable(Level.SEVERE))
            LOGGER.log(Level.SEVERE, t.getLocalizedMessage(), t);

        }

        @Override
        public void getNotification(ImageMosaicEventHandlers.ProcessingEvent event) {
          if (LOGGER.isLoggable(Level.FINE))
            LOGGER.fine(event.getMessage());

        }

      };
      eventHandler.addProcessingEventListener(listener);
      walker.run();
    } catch (Throwable e) {
      LOGGER.log(Level.SEVERE, "Unable to build mosaic", e);
      return false;
    } finally {
      catalogHandler.dispose();
    }

    // check that nothing bad happened
    if (exceptions.size() > 0)
      return false;
    return true;
  }

  // Make additional filters pluggable
    private static IOFileFilter initCleanUpFilter() {
        IOFileFilter filesFilter = FileFilterUtils.or(
                FileFilterUtils.suffixFileFilter("properties"),
                FileFilterUtils.suffixFileFilter("shp"), FileFilterUtils.suffixFileFilter("dbf"),
                FileFilterUtils.suffixFileFilter("sbn"), FileFilterUtils.suffixFileFilter("sbx"),
                FileFilterUtils.suffixFileFilter("shx"), FileFilterUtils.suffixFileFilter("qix"),
                FileFilterUtils.suffixFileFilter("lyr"), FileFilterUtils.suffixFileFilter("prj"),
                FileFilterUtils.suffixFileFilter("ncx"), FileFilterUtils.suffixFileFilter("gbx9"),
                FileFilterUtils.nameFileFilter("error.txt"),
                FileFilterUtils.nameFileFilter("_metadata"),
                FileFilterUtils.suffixFileFilter("sample_image"),
                FileFilterUtils.nameFileFilter("error.txt.lck"),
                FileFilterUtils.suffixFileFilter("xml"),
                FileFilterUtils.suffixFileFilter("db"));
        return filesFilter;
    }

    public static String getMessageFromException(Exception exception) {
    if (exception.getLocalizedMessage() != null)
      return exception.getLocalizedMessage();
    else
      return exception.getMessage();
  }

//  static URL checkSource(Object source) throws MalformedURLException,
//      DataSourceException {
//    return checkSource(source, null);
//  }

 

  static MosaicConfigurationBean loadMosaicProperties(final URL sourceURL,
                final String defaultLocationAttribute) {
      return loadMosaicProperties(sourceURL, defaultLocationAttribute, null);
  }
 
  static MosaicConfigurationBean loadMosaicProperties(
          final URL sourceURL,
    final String defaultLocationAttribute,
    final Set<String> ignorePropertiesSet) {
   
    if(LOGGER.isLoggable(Level.FINE)){
      LOGGER.log(Level.FINE,"Trying to load properties file from URL:"+sourceURL);
    }
   
    // ret value
    final MosaicConfigurationBean retValue = new MosaicConfigurationBean();
    final CatalogConfigurationBean catalogConfigurationBean = new CatalogConfigurationBean();
    retValue.setCatalogConfigurationBean(catalogConfigurationBean);
    final boolean ignoreSome = ignorePropertiesSet != null && !ignorePropertiesSet.isEmpty();

        //
        // load the properties file
        //
        URL propsURL = sourceURL;
        if (!sourceURL.toExternalForm().endsWith(".properties")) {
            propsURL = DataUtilities.changeUrlExt(sourceURL, "properties");
            if (propsURL.getProtocol().equals("file")) {
                final File sourceFile = DataUtilities.urlToFile(propsURL);
                if (!sourceFile.exists()) {
                    if (LOGGER.isLoggable(Level.INFO)) {
                        LOGGER.info("properties file doesn't exist");
                    }
                    return null;
                }
            }
        }
   
    final Properties properties = loadPropertiesFromURL(propsURL);
    if (properties == null) {
      if (LOGGER.isLoggable(Level.INFO))
        LOGGER.info("Unable to load mosaic properties file");
      return null;
    }

    String[] pairs = null;
    String pair[] = null;
   
    //
    // imposed bbox is optional
    //             
    if (!ignoreSome || !ignorePropertiesSet.contains(Prop.ENVELOPE2D)) {
      String bboxString = properties.getProperty(Prop.ENVELOPE2D, null);
      if(bboxString!=null){
        bboxString=bboxString.trim();
        try{
          ReferencedEnvelope bbox = parseEnvelope(bboxString);
          if(bbox!=null)
            retValue.setEnvelope(bbox);
          else
            if (LOGGER.isLoggable(Level.INFO))
              LOGGER.info("Cannot parse imposed bbox.");
        }catch (Exception e) {
          if (LOGGER.isLoggable(Level.INFO))
            LOGGER.log(Level.INFO,"Cannot parse imposed bbox.",e);
        }
      }
       
    }
   
    if (!ignoreSome
                        || !ignorePropertiesSet.contains(Prop.AUXILIARY_FILE)) {
                    retValue.setAuxiliaryFilePath(properties.getProperty(Prop.AUXILIARY_FILE));
        }

                if (!ignoreSome || !ignorePropertiesSet.contains(Prop.CHECK_AUXILIARY_METADATA)) {
                    final boolean checkAuxiliaryMetadata = Boolean.valueOf(properties.getProperty(
                            Prop.CHECK_AUXILIARY_METADATA, "false").trim());
                    retValue.setCheckAuxiliaryMetadata(checkAuxiliaryMetadata);
                }
               
   
    //
    // resolutions levels
    //             
    if (!ignoreSome || !ignorePropertiesSet.contains(Prop.LEVELS)) {
      int levelsNumber = Integer.parseInt(properties.getProperty(Prop.LEVELS_NUM, "1").trim());
      retValue.setLevelsNum(levelsNumber);
      if (!properties.containsKey(Prop.LEVELS)) {
        if (LOGGER.isLoggable(Level.INFO))
          LOGGER.info("Required key Levels not found.");
        return null;
      }
      final String levels = properties.getProperty(Prop.LEVELS).trim();
      pairs = levels.split(" ");
      if (pairs == null || pairs.length != levelsNumber) {
        if (LOGGER.isLoggable(Level.INFO))
          LOGGER.info("Levels number is different from the provided number of levels resoltion.");
        return null;
      }
      final double[][] resolutions = new double[levelsNumber][2];
      for (int i = 0; i < levelsNumber; i++) {
        pair = pairs[i].split(",");
        if (pair == null || pair.length != 2) {
          if (LOGGER.isLoggable(Level.INFO))
            LOGGER.info("OverviewLevel number is different from the provided number of levels resoltion.");
          return null;
        }
        resolutions[i][0] = Double.parseDouble(pair[0]);
        resolutions[i][1] = Double.parseDouble(pair[1]);
      }
      retValue.setLevels(resolutions);
    }

    //
    // typename, is mandatory when we don't use shapeiles
    //             
    if (!ignoreSome || !ignorePropertiesSet.contains(Prop.TYPENAME)) {
      String typeName = properties.getProperty(Prop.TYPENAME, null);
      catalogConfigurationBean.setTypeName(typeName);
    }

    //
    // suggested spi is optional
    //
    if (!ignoreSome || !ignorePropertiesSet.contains(Prop.SUGGESTED_SPI)) {
      if (properties.containsKey(Prop.SUGGESTED_SPI)) {
        final String suggestedSPI = properties.getProperty(
            Prop.SUGGESTED_SPI).trim();
        catalogConfigurationBean.setSuggestedSPI(suggestedSPI);
      }
    }

    //
    // time attribute is optional
    //
    if (properties.containsKey(Prop.TIME_ATTRIBUTE)) {
            final String timeAttribute = properties.getProperty("TimeAttribute").trim();
      retValue.setTimeAttribute(timeAttribute);
    }

    //
    // elevation attribute is optional
    //
    if (properties.containsKey(Prop.ELEVATION_ATTRIBUTE)) {
            final String elevationAttribute = properties.getProperty(Prop.ELEVATION_ATTRIBUTE).trim();
      retValue.setElevationAttribute(elevationAttribute);
    }

    //
                // additional domain attribute is optional
                //
                if (properties.containsKey(Prop.ADDITIONAL_DOMAIN_ATTRIBUTES)) {
                        final String additionalDomainAttributes = properties.getProperty(Prop.ADDITIONAL_DOMAIN_ATTRIBUTES).trim();
                        retValue.setAdditionalDomainAttributes(additionalDomainAttributes);
                }

    //
    // caching
    //
    if (properties.containsKey(Prop.CACHING)) {
      String caching = properties.getProperty(Prop.CACHING).trim();
      try {
          catalogConfigurationBean.setCaching(Boolean.valueOf(caching));
      } catch (Throwable e) {
          catalogConfigurationBean.setCaching(Boolean.valueOf(Utils.DEFAULT_CACHING_BEHAVIOR));
      }
    }

    //
    // name is not optional
    //
    if (!ignoreSome || !ignorePropertiesSet.contains(Prop.NAME)){
                    if(!properties.containsKey(Prop.NAME)) {
                            if(LOGGER.isLoggable(Level.SEVERE))
                                    LOGGER.severe("Required key Name not found.");         
                            return null;
                    }                      
                    String coverageName = properties.getProperty(Prop.NAME).trim();
                    retValue.setName(coverageName);
                }

    // need a color expansion?
    // this is a newly added property we have to be ready to the case where
    // we do not find it.
    if (!ignoreSome || !ignorePropertiesSet.contains(Prop.EXP_RGB)) {
      final boolean expandMe = Boolean.valueOf(properties.getProperty(
          Prop.EXP_RGB, "false").trim());
      retValue.setExpandToRGB(expandMe);
    }
   
    //
    // Is heterogeneous granules mosaic
    //
                if (!ignoreSome || !ignorePropertiesSet.contains(Prop.HETEROGENEOUS)) {
                    final boolean heterogeneous = Boolean.valueOf(properties.getProperty(
                            Prop.HETEROGENEOUS, "false").trim());
                    catalogConfigurationBean.setHeterogeneous(heterogeneous);
                }

    //
    // Absolute or relative path
    //
    if (!ignoreSome || !ignorePropertiesSet.contains(Prop.ABSOLUTE_PATH)) {
      final boolean absolutePath = Boolean.valueOf(properties
          .getProperty(Prop.ABSOLUTE_PATH,
              Boolean.toString(Utils.DEFAULT_PATH_BEHAVIOR))
          .trim());
      catalogConfigurationBean.setAbsolutePath(absolutePath);
    }

    //
    // Footprint management
    //
    if (!ignoreSome
        || !ignorePropertiesSet.contains(Prop.FOOTPRINT_MANAGEMENT)) {
      final boolean footprintManagement = Boolean.valueOf(properties
          .getProperty(Prop.FOOTPRINT_MANAGEMENT, "false").trim());
      retValue.setFootprintManagement(footprintManagement);
    }

    //
    // location
    // 
    if (!ignoreSome
        || !ignorePropertiesSet.contains(Prop.LOCATION_ATTRIBUTE)) {
        catalogConfigurationBean.setLocationAttribute(properties.getProperty(
          Prop.LOCATION_ATTRIBUTE, Utils.DEFAULT_LOCATION_ATTRIBUTE).trim());
    }
   
    // return value
    return retValue;
  }

  /**
   * Parses a bbox in the form of MIX,MINY MAXX,MAXY
   * @param bboxString the string to parse the bbox from
   * @return a {@link ReferencedEnvelope} with the parse bbox or null
   */
  public static ReferencedEnvelope parseEnvelope(final String bboxString) {
    if(bboxString==null||bboxString.length()==0)
      return null;
   
    final String[] pairs = bboxString.split(" ");
    if (pairs != null &&pairs.length == 2) {
   
      String[] pair1 = pairs[0].split(",");
      String[] pair2 = pairs[1].split(",");
      if(pair1!=null&&pair1.length==2&&pair2!=null&&pair2.length==2)
        return new ReferencedEnvelope(
             Double.parseDouble(pair1[0]),
             Double.parseDouble(pair2[0]),
             Double.parseDouble(pair1[1]),
             Double.parseDouble(pair2[1]),
             null);
 
    }
    // something bad happened
    return null;
  }

  public static Properties loadPropertiesFromURL(URL propsURL) {
    final Properties properties = new Properties();
    InputStream stream = null;
    InputStream openStream = null;
    try {
      openStream = propsURL.openStream();
      stream = new BufferedInputStream(openStream);
      properties.load(stream);
    } catch (FileNotFoundException e) {
      if (LOGGER.isLoggable(Level.SEVERE))
        LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
      return null;
    } catch (IOException e) {
      if (LOGGER.isLoggable(Level.SEVERE))
        LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
      return null;
    } finally {

      if (stream != null)
        IOUtils.closeQuietly(stream);

      if (openStream != null)
        IOUtils.closeQuietly(openStream);

    }
    return properties;
  }

  public static IOFileFilter excludeFilters(final IOFileFilter inputFilter,
      IOFileFilter... filters) {
    IOFileFilter retFilter = inputFilter;
    for (IOFileFilter filter : filters) {
      retFilter = FileFilterUtils.and(retFilter,
          FileFilterUtils.notFileFilter(filter));
    }
    return retFilter;
  }

  /**
   * Look for an {@link ImageReader} instance that is able to read the
   * provided {@link ImageInputStream}, which must be non null.
   *
   * <p>
   * In case no reader is found, <code>null</code> is returned.
   *
   * @param inStream
   *            an instance of {@link ImageInputStream} for which we need to
   *            find a suitable {@link ImageReader}.
   * @return a suitable instance of {@link ImageReader} or <code>null</code>
   *         if one cannot be found.
   */
  static ImageReader getReader(final ImageInputStream inStream) {
    Utilities.ensureNonNull("inStream", inStream);
    // get a reader
    inStream.mark();
    final Iterator<ImageReader> readersIt = ImageIO
        .getImageReaders(inStream);
    if (!readersIt.hasNext()) {
      return null;
    }
    return readersIt.next();
  }

  /**
   * Retrieves the dimensions of the {@link RenderedImage} at index
   * <code>imageIndex</code> for the provided {@link ImageReader} and
   * {@link ImageInputStream}.
   *
   * <p>
   * Notice that none of the input parameters can be <code>null</code> or a
   * {@link NullPointerException} will be thrown. Morevoer the
   * <code>imageIndex</code> cannot be negative or an
   * {@link IllegalArgumentException} will be thrown.
   *
   * @param imageIndex
   *            the index of the image to get the dimensions for.
   * @param inStream
   *            the {@link ImageInputStream} to use as an input
   * @param reader
   *            the {@link ImageReader} to decode the image dimensions.
   * @return a {@link Rectangle} that contains the dimensions for the image at
   *         index <code>imageIndex</code>
   * @throws IOException
   *             in case the {@link ImageReader} or the
   *             {@link ImageInputStream} fail.
   */
  static Rectangle getDimension(final int imageIndex,final ImageReader reader)
      throws IOException {
    Utilities.ensureNonNull("reader", reader);
    if (imageIndex < 0)
      throw new IllegalArgumentException(Errors.format(
          ErrorKeys.INDEX_OUT_OF_BOUNDS_$1, imageIndex));
    return new Rectangle(0, 0, reader.getWidth(imageIndex), reader
        .getHeight(imageIndex));
  }

  /**
   * Default priority for the underlying {@link Thread}.
   */
  public static final int DEFAULT_PRIORITY = Thread.NORM_PRIORITY;
  /**
   * Default location attribute name.
   */
  public static final String DEFAULT_LOCATION_ATTRIBUTE = "location";

  public static final String DEFAULT_INDEX_NAME = "index";

  /**
   * Checks that a {@link File} is a real file, exists and is readable.
   *
   * @param file
   *            the {@link File} instance to check. Must not be null.
   *
   * @return <code>true</code> in case the file is a real file, exists and is
   *         readable; <code>false </code> otherwise.
   */
  public static boolean checkFileReadable(final File file) {
    if (LOGGER.isLoggable(Level.FINE)) {
      final String message = getFileInfo(file);
      LOGGER.fine(message);
    }
    if (!file.exists() || !file.canRead() || !file.isFile())
      return false;
    return true;
  }

    /**
     * Creates a human readable message that describe the provided {@link File} object in terms of its properties.
     *
     * <p>
     * Useful for creating meaningful log messages.
     *
     * @param file the {@link File} object to create a descriptive message for
     * @return a {@link String} containing a descriptive message about the provided {@link File}.
     *
     */
    public static String getFileInfo(final File file) {
        final StringBuilder builder = new StringBuilder();
        builder.append("Checking file:").append(
            FilenameUtils.getFullPath(file.getAbsolutePath())).append(
            "\n");
        builder.append("isHidden:").append(file.isHidden()).append("\n");
        builder.append("exists:").append(file.exists()).append("\n");
        builder.append("isFile").append(file.isFile()).append("\n");
        builder.append("canRead:").append(file.canRead()).append("\n");       
        builder.append("canWrite").append(file.canWrite()).append("\n");
        builder.append("canExecute:").append(file.canExecute()).append("\n");
        builder.append("isAbsolute:").append(file.isAbsolute()).append("\n");
        builder.append("lastModified:").append(file.lastModified()).append("\n");
        builder.append("length:").append(file.length());
        final String message=builder.toString();
        return message;
    }

  /**
   * @param testingDirectory
   * @return
   * @throws IllegalArgumentException
   * @throws IOException
   */
  public static String checkDirectory(String testingDirectory, boolean writable)
      throws IllegalArgumentException {
     
    File inDir = new File(testingDirectory);
    boolean failure= !inDir.exists()||!inDir.isDirectory()||inDir.isHidden()||!inDir.canRead();
    if(writable){
        failure|=!inDir.canWrite();
    }
    if (failure) {
            String message="Unable to create the mosaic\n"+
                        "location is:"+testingDirectory+"\n"+
                        "location exists:"+inDir.exists()+"\n"+
                        "location is a directory:"+inDir.isDirectory()+"\n"+
                        "location is writable:"+inDir.canWrite()+"\n"+
                        "location is readable:"+inDir.canRead()+"\n"+
                        "location is hidden:"+inDir.isHidden()+"\n";
      LOGGER.severe(message);
      throw new IllegalArgumentException(message);
    }
    try {
      testingDirectory = inDir.getCanonicalPath();
    } catch (IOException e) {
      throw new IllegalArgumentException(e);
    }
    testingDirectory = FilenameUtils.normalize(testingDirectory);
    if (!testingDirectory.endsWith(File.separator))
      testingDirectory = testingDirectory + File.separator;
    // test to see if things are still good
    inDir = new File(testingDirectory);
    failure= !inDir.exists()||!inDir.isDirectory()||inDir.isHidden()||!inDir.canRead();
                if(writable){
                    failure|=!inDir.canWrite();
                }
                if (failure) {
                        String message="Unable to create the mosaic\n"+
                        "location is:"+testingDirectory+"\n"+
                        "location exists:"+inDir.exists()+"\n"+
                        "location is a directory:"+inDir.isDirectory()+"\n"+
                        "location is writable:"+inDir.canWrite()+"\n"+
                        "location is readable:"+inDir.canRead()+"\n"+
                        "location is hidden:"+inDir.isHidden()+"\n";
                        LOGGER.severe(message);
                        throw new IllegalArgumentException(message);
                }
    return testingDirectory;
  }

  static boolean checkURLReadable(URL url) {
    try {
      url.openStream().close();
    } catch (Exception e) {
      return false;
    }
    return true;
  }

  public static final DataStoreFactorySpi SHAPE_SPI = new ShapefileDataStoreFactory();

  static final String DIRECT_KAKADU_PLUGIN = "it.geosolutions.imageio.plugins.jp2k.JP2KKakaduImageReader";

  public static final boolean DEFAULT_RECURSION_BEHAVIOR = true;

  /**
   *
   * @param datastoreProperties
   * @return
   * @throws IOException
   */
  public static Map<String, Serializable> createDataStoreParamsFromPropertiesFile(
      final URL datastoreProperties)
      throws IOException {
    // read the properties file
    Properties properties = loadPropertiesFromURL(datastoreProperties);
    if (properties == null)
      return null;

    // SPI
    final String SPIClass = properties.getProperty("SPI");
    try {
      // create a datastore as instructed
      final DataStoreFactorySpi spi = (DataStoreFactorySpi) Class.forName(SPIClass).newInstance();
      return createDataStoreParamsFromPropertiesFile(properties, spi);
    } catch (Exception e) {
      final IOException ioe = new IOException();
      throw (IOException) ioe.initCause(e);
    }
  }

  /**
   * Store a sample image from which we can derive the default SM and CM
   *
   * @param sampleImageFile
   *            where we should store the image
   * @param defaultSM
   *            the {@link SampleModel} for the sample image.
   * @param defaultCM
   *            the {@link ColorModel} for the sample image.
   * @throws IOException
   *             in case something bad occurs during writing.
   */
  public static void storeSampleImage(final File sampleImageFile,
      final SampleModel defaultSM, final ColorModel defaultCM)
      throws IOException {
    // create 1X1 image
    final SampleModel sm = defaultSM.createCompatibleSampleModel(1, 1);
    final WritableRaster raster = RasterFactory.createWritableRaster(sm,
        null);
    final BufferedImage sampleImage = new BufferedImage(defaultCM, raster,
        defaultCM.isAlphaPremultiplied(), null);

    // serialize it
    OutputStream outStream = null;
    ObjectOutputStream ooStream = null;
    SerializableRenderedImage sri = null;
    try {
      outStream = new BufferedOutputStream(new FileOutputStream(
          sampleImageFile));
      ooStream = new ObjectOutputStream(outStream);
      sri = new SerializableRenderedImage(sampleImage, true);
      ooStream.writeObject(sri);
    } finally {
      try {
        if (ooStream != null)
          ooStream.close();
      } catch (Throwable e) {
        IOUtils.closeQuietly(ooStream);
      }
      try {
        if (outStream != null)
          outStream.close();
      } catch (Throwable e) {
        IOUtils.closeQuietly(outStream);
      }
      try {
                            if (sri != null)
                                    sri.dispose();
                        } catch (Throwable e) {
                        }
    }
  }

  /**
   * Load a sample image from which we can take the sample model and color
   * model to be used to fill holes in responses.
   *
   * @param sampleImageFile
   *            the path to sample image.
   * @return a sample image from which we can take the sample model and color
   *         model to be used to fill holes in responses.
   */
  public static RenderedImage loadSampleImage(final File sampleImageFile) {
    // serialize it
    InputStream inStream = null;
    ObjectInputStream oiStream = null;
    try {

      // do we have the sample image??
      if (Utils.checkFileReadable(sampleImageFile)) {
        inStream = new BufferedInputStream(new FileInputStream(
            sampleImageFile));
        oiStream = new ObjectInputStream(inStream);

        // load the image
        return (RenderedImage) oiStream.readObject();

      } else {
        if (LOGGER.isLoggable(Level.WARNING))
          LOGGER.warning("Unable to find sample image for path "
              + sampleImageFile);
        return null;
      }
    } catch (FileNotFoundException e) {
      if (LOGGER.isLoggable(Level.WARNING))
        LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
      return null;
    } catch (IOException e) {
      if (LOGGER.isLoggable(Level.WARNING))
        LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
      return null;
    } catch (ClassNotFoundException e) {
      if (LOGGER.isLoggable(Level.WARNING))
        LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
      return null;
    } finally {
      try {
        if (inStream != null)
          inStream.close();
      } catch (Throwable e) {

        if (LOGGER.isLoggable(Level.FINE))
          LOGGER.log(Level.FINE, e.getLocalizedMessage(), e);
      }
      try {
        if (oiStream != null)
          oiStream.close();
      } catch (Throwable e) {

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

  /**
   * A transparent color for missing data.
   */
  static final Color TRANSPARENT = new Color(0,0,0,0);
       
  // final static Boolean IGNORE_FOOTPRINT = Boolean.getBoolean("org.geotools.footprint.ignore");
 
    public static final boolean DEFAULT_FOOTPRINT_MANAGEMENT = true;
 
  public static final boolean DEFAULT_CONFIGURATION_CACHING = false;

            public static Map<String, Serializable> createDataStoreParamsFromPropertiesFile(
      Properties properties, DataStoreFactorySpi spi) throws IOException {
    // get the params
    final Map<String, Serializable> params = new HashMap<String, Serializable>();
    final Param[] paramsInfo = spi.getParametersInfo();
    for (Param p : paramsInfo) {
      // search for this param and set the value if found
      if (properties.containsKey(p.key)){
          params.put(p.key, (Serializable) Converters.convert(properties.getProperty(p.key), p.type));
      }
      else if (p.required && p.sample == null)
        throw new IOException("Required parameter missing: "+ p.toString());
    }
   
    return params;
  }
           
    public static Map<String, Serializable> filterDataStoreParams(
            Properties properties, DataStoreFactorySpi spi) throws IOException {
        // get the params
        final Map<String, Serializable> params = new HashMap<String, Serializable>();
        final Param[] paramsInfo = spi.getParametersInfo();
        for (Param p : paramsInfo) {
            // search for this param and set the value if found
            if (properties.containsKey(p.key)) {
                params.put(p.key,
                        (Serializable) Converters.convert(properties.get(p.key), p.type));
            } else if (p.required && p.sample == null)
                throw new IOException("Required parameter missing: " + p.toString());
        }

        return params;
    }

    static URL checkSource(Object source, Hints hints) {
        URL sourceURL = null;
        File sourceFile = null;

        //
        // Check source
        //
        // if it is a URL or a String let's try to see if we can get a file to
        // check if we have to build the index
        if (source instanceof File) {
                sourceFile = (File) source;
                sourceURL = DataUtilities.fileToURL(sourceFile);
        } else if (source instanceof URL) {
            sourceURL = (URL) source;
            if (sourceURL.getProtocol().equals("file")) {
                sourceFile = DataUtilities.urlToFile(sourceURL);
            }
        } else if (source instanceof String) {
                // is it a File?
                final String tempSource = (String) source;
                File tempFile = new File(tempSource);
                if (!tempFile.exists()) {
                        // is it a URL
                        try {
                                sourceURL = new URL(tempSource);
                                source = DataUtilities.urlToFile(sourceURL);
                        } catch (MalformedURLException e) {
                                sourceURL = null;
                                source = null;
                        }
                } else {
                        sourceURL = DataUtilities.fileToURL(tempFile);

                        // so that we can do our magic here below
                        sourceFile = tempFile;
                }
        }
        // //
        //
        // at this point we have tried to convert the thing to a File as hard as
        // we could, let's see what we can do
        //
        // //
        if (sourceFile != null) {
                if (!sourceFile.isDirectory())
                        // real file, can only be a shapefile at this stage or a
                        // datastore.properties file
                        sourceURL = DataUtilities.fileToURL((File) sourceFile);
                else {
                        // it's a DIRECTORY, let's look for a possible properties files
                        // that we want to load
                        final String locationPath = sourceFile.getAbsolutePath();
                        final String defaultIndexName = getDefaultIndexName(locationPath);
                        boolean datastoreFound = false;
                        boolean buildMosaic = false;

                        //
                        // do we have a datastore properties file? It will preempt on
                        // the shapefile
                        // TODO: Refactor these checks once we integrate datastore on indexer.xml
                        //
                        File dataStoreProperties = new File(locationPath,"datastore.properties");
//                        File emptyFile = new File(locationPath,"empty");

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

                        // do we have a valid datastore + mosaic properties pair?
                        if (Utils.checkFileReadable(dataStoreProperties)) {
                                // we have a datastore.properties file
                                datastoreFound = true;

                                // check the first valid mosaic properties
                                boolean found = false;
                                for (File propFile : properties)
                                        if (Utils.checkFileReadable(propFile)) {
                                                // load it
                                                if (null != Utils.loadMosaicProperties(DataUtilities.fileToURL(propFile),Utils.DEFAULT_LOCATION_ATTRIBUTE)) {
                                                        found = true;
                                                        break;
                                                }
                                        }

                                // we did not find any good candidate for mosaic.properties
                                // file, this will signal it
                                if (!found)
                                        buildMosaic = true;

                        } else
                        {
                                // we did not find any good candidate for mosaic.properties
                                // file, this will signal it
                                buildMosaic = true;
                                datastoreFound = false;
                        }

                        //
                        // now let's try with shapefile and properties couple
                        //
                        File shapeFile = null;
                        if (!datastoreFound) {
                                for (File propFile : properties) {

                                        // load properties
                                        if (null == Utils.loadMosaicProperties(DataUtilities.fileToURL(propFile), Utils.DEFAULT_LOCATION_ATTRIBUTE))
                                                continue;

                                        // look for a couple shapefile, mosaic properties file
                                        shapeFile = new File(locationPath, FilenameUtils.getBaseName(propFile.getName())+ ".shp");
                                        if (!Utils.checkFileReadable(shapeFile)&& Utils.checkFileReadable(propFile))
                                                buildMosaic = true;
                                        else {
                                                buildMosaic = false;
                                                break;
                                        }
                                }

                        }

                        // did we find anything? If no, we try to build a new mosaic
                        if (buildMosaic) {
                                ////
                                //
                                // Creating a new mosaic
                                //
                                ////
                                // try to build a mosaic inside this directory and see what
                                // happens
                           
                                // preliminar checks
                                final File mosaicDirectory= new File(locationPath);
                                if(!mosaicDirectory.exists()||mosaicDirectory.isFile()||!mosaicDirectory.canWrite()){
                                    if(LOGGER.isLoggable(Level.SEVERE)){
                                        LOGGER.log(Level.SEVERE,"Unable to create the mosaic, check the location:\n"+
                                                "location is:"+locationPath+"\n"+
                                                "location exists:"+mosaicDirectory.exists()+"\n"+
                                                "location is a directory:"+mosaicDirectory.isDirectory()+"\n"+
                                                "location is writable:"+mosaicDirectory.canWrite()+"\n"+
                                                "location is readable:"+mosaicDirectory.canRead()+"\n"+
                                                "location is hidden:"+mosaicDirectory.isHidden()+"\n");
                                    }
                                    return null;
                                }
                           
                                // actual creation
                                createMosaic(locationPath, defaultIndexName, DEFAULT_WILCARD, DEFAULT_PATH_BEHAVIOR, hints);

                                // check that the mosaic properties file was created
                                final File propertiesFile = new File(locationPath,
                                                defaultIndexName + ".properties");
                                if (!Utils.checkFileReadable(propertiesFile)) {
                                        // retrieve a null so that we shows that a problem occurred
                                        if (!checkMosaicHasBeenInitialized(locationPath, defaultIndexName)) {
                                            sourceURL = null;
                                            return sourceURL;
                                        }
                                }

                                // check that the shapefile was correctly created in case it
                                // was needed
                                sourceURL = updateSourceURL(sourceURL, datastoreFound, locationPath, defaultIndexName/*, emptyFile*/);
                               
                        } else
                                // now set the new source and proceed
                                sourceURL = datastoreFound ? DataUtilities.fileToURL(dataStoreProperties) : DataUtilities.fileToURL(shapeFile);

                }
        } else {
                // SK: We don't set SourceURL to null now, just because it doesn't
                // point to a file
                // sourceURL=null;
        }
        return sourceURL;
    }

    private static String getDefaultIndexName(final String locationPath) {
        if(locationPath == null) {
            return null;
        }
        File file = new File(locationPath);
        if(file.isDirectory()) {
            File indexer = new File(file, "indexer.properties");
            if(indexer.exists()) {
                URL indexerUrl = DataUtilities.fileToURL(indexer);
                Properties config = Utils.loadPropertiesFromURL(indexerUrl);
                if(config != null && config.get(Utils.Prop.NAME) != null) {
                    return (String) config.get(Utils.Prop.NAME);
                }
            }
        }
       
        return FilenameUtils.getName(locationPath);
    }
   

    /**
     * Look for a proper sourceURL to be returned.
     *
     * @param sourceURL
     * @param datastoreFound
     * @param locationPath
     * @param defaultIndexName
     * @param emptyFile
     * @return
     */
    private static URL updateSourceURL(URL sourceURL, boolean datastoreFound, String locationPath, String defaultIndexName/*,
            File emptyFile*/) {
        if (!datastoreFound) {
            File shapeFile = new File(locationPath, defaultIndexName + ".shp");

            if (!Utils.checkFileReadable(shapeFile)) {
//                if (!Utils.checkFileReadable(emptyFile)) {
                    sourceURL = null;
//                } else {
//                    sourceURL = DataUtilities.fileToURL(emptyFile);
//                }
            } else {
                // now set the new source and proceed
                sourceURL = DataUtilities.fileToURL(shapeFile);
            }
        } else {
                File dataStoreProperties = new File(locationPath,"datastore.properties");

                // datastore.properties as the source
                if (!Utils.checkFileReadable(dataStoreProperties)){
                    sourceURL = null;
                }
                else {
                    sourceURL = DataUtilities.fileToURL(dataStoreProperties);
                }
        }

      
        return sourceURL;
    }

    private static boolean checkMosaicHasBeenInitialized(String locationPath, String defaultIndexName) {
        File mosaicFile = new File(locationPath, defaultIndexName + ".xml");
        if (Utils.checkFileReadable(mosaicFile)) {
            return true;
        }
        mosaicFile = new File(locationPath, defaultIndexName + ".properties");
        if (Utils.checkFileReadable(mosaicFile)) {
            return true;
        }
       
        return false;
    }

    static final double SAMEBBOX_THRESHOLD_FACTOR = 20;

    static final double AFFINE_IDENTITY_EPS = 1E-6;

    public static final boolean DEFAULT_COLOR_EXPANSION_BEHAVIOR = false;

    public static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC");

    static final String DESCENDING_ORDER_IDENTIFIER = " D"; // SortOrder.DESCENDING.identifier();

    static final String ASCENDING_ORDER_IDENTIFIER = " A"; // SortOrder.ASCENDING.identifier();

    public static final String SCAN_FOR_TYPENAMES = "TypeNames";

    public static final String SAMPLE_IMAGE_NAME = "sample_image";

    public static final String TIME_DOMAIN = "TIME";

    public static final String ELEVATION_DOMAIN = "ELEVATION";

    public static final String ADDITIONAL_DOMAIN = "ADDITIONAL";

    public static ObjectFactory OBJECT_FACTORY = new ObjectFactory();

    private static IOFileFilter CLEANUP_FILTER;

    /**
     * Private constructor to initialize the ehCache instance. It can be configured through a Bean.
     *
     * @param ehcache
     */
    private Utils(Cache ehcache) {
        Utils.ehcache = ehcache;
    }
   
    /**
     * Setup a {@link Histogram} object by deserializing 
     * a file representing a serialized Histogram.
     *
     * @param file
     * @return the deserialized histogram.
     */
    static Histogram getHistogram(final String file){
        Utilities.ensureNonNull("file", file);
        Histogram histogram = null;
       
        // Firstly: check if the histogram have been already
        // deserialized and it is available in cache
        if (ehcache != null && ehcache.isKeyInCache(file)){
            if (ehcache.isElementInMemory(file)){
                final Element element = ehcache.get(file);
                if (element != null){
                    final Serializable value = element.getValue();
                    if (value != null && value instanceof Histogram){
                        histogram = (Histogram) value;
                        return histogram;
                    }
                }
            }
        }
       
        // No histogram in cache. Deserializing...
        if (histogram == null){
            FileInputStream fileStream = null;
            ObjectInputStream objectStream = null;
            try {

                fileStream = new FileInputStream(file);
                objectStream = new ObjectInputStream(fileStream);
                histogram = (Histogram) objectStream.readObject();
                if (ehcache != null){
                    ehcache.put(new Element(file, histogram));
                }
            } catch (FileNotFoundException e) {
                if (LOGGER.isLoggable(Level.FINE)){
                    LOGGER.fine("Unable to parse Histogram:" + e.getLocalizedMessage());
                }
            } catch (IOException e) {
                if (LOGGER.isLoggable(Level.FINE)){
                    LOGGER.fine("Unable to parse Histogram:" + e.getLocalizedMessage());
                }
            } catch (ClassNotFoundException e) {
                if (LOGGER.isLoggable(Level.FINE)){
                    LOGGER.fine("Unable to parse Histogram:" + e.getLocalizedMessage());
                }
            } finally {
                if (objectStream != null){
                    IOUtils.closeQuietly(objectStream);
                }
                if (fileStream != null){
                    IOUtils.closeQuietly(fileStream);
                }
            }
        }
        return histogram;
    }

    /**
         * Check if the provided granule's footprint covers the same area of the granule's bbox.
         * @param granuleFootprint the granule Footprint
         * @param granuleBBOX the granule bbox
         * @return {@code true} in case the footprint isn't covering the FULL granule's bbox.
         */
        static boolean areaIsDifferent(
                final Geometry granuleFootprint,
                final AffineTransform baseGridToWorld,
                final ReferencedEnvelope granuleBBOX) {
           
            // //
            //
            // First preliminar check:
            // check if the footprint's bbox corners are the same of the granule's bbox
            // (Using a threshold)
            //
            // //
            final Envelope envelope = granuleFootprint.getEnvelope().getEnvelopeInternal();
            double deltaMinX = Math.abs(envelope.getMinX() - granuleBBOX.getMinX());
            double deltaMinY = Math.abs(envelope.getMinY() - granuleBBOX.getMinY());
            double deltaMaxX = Math.abs(envelope.getMaxX() - granuleBBOX.getMaxX());
            double deltaMaxY = Math.abs(envelope.getMaxY() - granuleBBOX.getMaxY());
            final double resX = XAffineTransform.getScaleX0(baseGridToWorld);
            final double resY = XAffineTransform.getScaleY0(baseGridToWorld);
            final double toleranceX = resX / Utils.SAMEBBOX_THRESHOLD_FACTOR;
            final double toleranceY = resY / Utils.SAMEBBOX_THRESHOLD_FACTOR;
           
            // Taking note of the area of a single cell
            final double cellArea = resX * resY;
   
            if (deltaMinX > toleranceX || deltaMaxX > toleranceX || deltaMinY > toleranceY || deltaMaxY > toleranceY){
                // delta exceed tolerance. Area is not the same         
                return true;
            }
           
            // //
            //
            // Second check:
            // Here, the footprint's bbox and the granule's bbox are equal.
            // However this is not enough:
            // - suppose the footprint is a diamond
            // - Create a rectangle by circumscribing the diamond
            // - If this rectangle match with the granule's bbox, this doesn't imply
            // that the diamond covers the same area of the bbox.
            // Therefore, we need to compute the area and compare them.
            //
            // //
            final double footprintArea = granuleFootprint.getArea();
            //final double bboxArea = granuleBBOX.getArea();
            final double bboxArea = granuleBBOX.getHeight() * granuleBBOX.getWidth();
                 
            // If 2 areas are different more than the cellArea, then they are not the same area
            if (Math.abs(footprintArea - bboxArea) > cellArea)
                return true;
            return false;
    }

    /**
     * Checks if the Shape equates to a Rectangle, if it does it performs a conversion, otherwise returns null
     *
     * @param shape
     * @return
     */
    static Rectangle toRectangle(Shape shape) {
        if (shape instanceof Rectangle) {
            return (Rectangle) shape;
        }

        if (shape == null) {
            return null;
        }

        // check if it's equivalent to a rectangle
        PathIterator iter = shape.getPathIterator(new AffineTransform());
        double[] coords = new double[2];

        // not enough points?
        if (iter.isDone()) {
            return null;
        }

        // get the first and init the data structures
        iter.next();
        int action = iter.currentSegment(coords);
        if (action != PathIterator.SEG_MOVETO && action != PathIterator.SEG_LINETO) {
            return null;
        }
        double minx = coords[0];
        double miny = coords[1];
        double maxx = minx;
        double maxy = miny;
        double prevx = minx;
        double prevy = miny;
        int i = 0;

        // at most 4 steps, if more it's not a strict rectangle
        for (; i < 4 && !iter.isDone(); i++) {
            iter.next();
            action = iter.currentSegment(coords);

            if (action == PathIterator.SEG_CLOSE) {
                break;
            }
            if (action != PathIterator.SEG_LINETO) {
                return null;
            }

            // check orthogonal step (x does not change and y does, or vice versa)
            double x = coords[0];
            double y = coords[1];
            if (!(prevx == x && prevy != y) && !(prevx != x && prevy == y)) {
                return null;
            }

            // update mins and maxes
            if (x < minx) {
                minx = x;
            } else if (x > maxx) {
                maxx = x;
            }
            if (y < miny) {
                miny = y;
            } else if (y > maxy) {
                maxy = y;
            }

            // keep track of prev step
            prevx = x;
            prevy = y;
        }

        // if more than 4 other points it's not a standard rectangle
        iter.next();
        if (!iter.isDone() || i != 3) {
            return null;
        }

        // turn it into a rectangle
        return new Rectangle2D.Double(minx, miny, maxx - minx, maxy - miny).getBounds();
    }

    public static ImageLayout getImageLayoutHint(RenderingHints renderHints) {
        if (renderHints == null||!renderHints.containsKey(JAI.KEY_IMAGE_LAYOUT)) {
            return null;
        } else {
            return (ImageLayout) renderHints.get(JAI.KEY_IMAGE_LAYOUT);
        }
    }

    public static TileCache getTileCacheHint(RenderingHints renderHints) {
        if (renderHints == null||!renderHints.containsKey(JAI.KEY_TILE_CACHE)) {
            return null;
        } else {
            return (TileCache) renderHints.get(JAI.KEY_TILE_CACHE);
        }
    }

    public static BorderExtender getBorderExtenderHint(RenderingHints renderHints) {
        if (renderHints == null||!renderHints.containsKey(JAI.KEY_BORDER_EXTENDER)) {
            return null;
        } else {
            return (BorderExtender) renderHints.get(JAI.KEY_BORDER_EXTENDER);
        }
    }
   
    public static TileScheduler getTileSchedulerHint(RenderingHints renderHints) {
        if (renderHints == null||!renderHints.containsKey(JAI.KEY_TILE_SCHEDULER)) {
            return null;
        } else {
            return (TileScheduler) renderHints.get(JAI.KEY_TILE_SCHEDULER);
        }
    }
   
    /**
     * Create a Range of numbers from a couple of values.
     * @param firstValue
     * @param secondValue
     * @return
     */
    public static Range<? extends Number> createRange(Object firstValue, Object secondValue) {
        Class<? extends Object> targetClass = firstValue.getClass();
        Class<? extends Object> target2Class = secondValue.getClass();
        if (targetClass != target2Class) {
            throw new IllegalArgumentException("The 2 values need to belong to the same class:\n"
                    + "firstClass = " + targetClass.toString() + "; secondClass = " + targetClass.toString());
        }
        if (targetClass == Byte.class) {
            return new Range<Byte>(Byte.class, (Byte) firstValue, (Byte) secondValue);
        } else if (targetClass == Short.class) {
            return new Range<Short>(Short.class, (Short) firstValue, (Short) secondValue);
        } else if (targetClass == Integer.class) {
            return new Range<Integer>(Integer.class, (Integer) firstValue, (Integer) secondValue);
        } else if (targetClass == Long.class) {
            return new Range<Long>(Long.class, (Long) firstValue, (Long) secondValue);
        } else if (targetClass == Float.class) {
            return new Range<Float>(Float.class, (Float) firstValue, (Float) secondValue);
        } else if (targetClass == Double.class) {
            return new Range<Double>(Double.class, (Double) firstValue, (Double) secondValue);
        } else return null;
    }
   
    /**
     * Simple minimal check which checks whether and indexer file exists
     * @param source
     * @return
     */
    public static boolean minimalIndexCheck(Object source) {
        File sourceFile = null;
        URL sourceURL = null;
        if (source instanceof File) {
            sourceFile = (File) source;
        } else if (source instanceof URL) {
            sourceURL = (URL) source;
            if (sourceURL.getProtocol().equals("file")) {
                sourceFile = DataUtilities.urlToFile(sourceURL);
            }
        } else if (source instanceof String) {
            // is it a File?
            final String tempSource = (String) source;
            File tempFile = new File(tempSource);
            if (!tempFile.exists()) {
                // is it a URL
                try {
                    sourceURL = new URL(tempSource);
                    source = DataUtilities.urlToFile(sourceURL);
                } catch (MalformedURLException e) {
                    sourceURL = null;
                    source = null;
                }
            } else {
                sourceURL = DataUtilities.fileToURL(tempFile);

                // so that we can do our magic here below
                sourceFile = tempFile;
            }
        }
        final File indexerProperties = new File(sourceFile, Utils.INDEXER_PROPERTIES);
        if (Utils.checkFileReadable(indexerProperties)) {
            return true;
        }
        final File indexerXML = new File(sourceFile, Utils.INDEXER_XML);
        if (Utils.checkFileReadable(indexerXML)) {
            return true;
        }
        return false;
    }

    /**
     * Check whether 2 resolution levels sets are homogeneous (within a tolerance)
     * @param numberOfLevels
     * @param resolutionLevels
     * @param compareLevels
     * @return
     */
    public static boolean homogeneousCheck(final int numberOfLevels, double[][] resolutionLevels,
            double[][] compareLevels) {
        for (int k = 0; k < numberOfLevels; k++) {
            if (Math.abs(resolutionLevels[k][0] - compareLevels[k][0]) > RESOLUTION_TOLERANCE_FACTOR
                    * compareLevels[k][0]
                    || Math.abs(resolutionLevels[k][1] - compareLevels[k][1]) > RESOLUTION_TOLERANCE_FACTOR
                            * compareLevels[k][1]) {
                return false;
            }
        }
        return true;
    }

    /**
     * Unmarshal the file and return and Indexer object.
     *
     * @param indexerFile
     * @return
     * @throws JAXBException
     */
    public static Indexer unmarshal(File indexerFile) throws JAXBException {
        Unmarshaller unmarshaller = null;
        Indexer indexer = null;
        if (indexerFile != null) {
            unmarshaller = CONTEXT.createUnmarshaller();
            indexer = (Indexer) unmarshaller.unmarshal(indexerFile);
        }
        return indexer;
    }

    /**
     * This method checks the {@link ColorModel} of the current image with the one of the first image in order to check if they are compatible or
     * not in order to perform a mosaic operation.
     *
     * <p>
     * It is worth to point out that we also check if, in case we have two index color model image, we also try to suggest whether or not we
     * should do a color expansion.
     *
     * @param defaultCM
     * @param defaultPalette
     * @param actualCM
     * @return a boolean asking to skip this feature.
     */
    static boolean checkColorModels(ColorModel defaultCM, byte[][] defaultPalette, MosaicConfigurationBean configuration, ColorModel actualCM) {
        //
        //
        // ComponentColorModel
        //
        //
       
        if (defaultCM instanceof ComponentColorModel && actualCM instanceof ComponentColorModel) {
            final ComponentColorModel defCCM = (ComponentColorModel) defaultCM, actualCCM = (ComponentColorModel) actualCM;
           
            // number of color components
            final int numColorComponents = defCCM.getNumColorComponents();
            if(numColorComponents != actualCCM.getNumColorComponents()){
                return false;
            }
           
            // componets size
            for(int i=0;i<numColorComponents;i++){
                if(defaultCM.getComponentSize(i)!=defaultCM.getComponentSize(i)){
                    return false;
                }
            }
            return !(defCCM.hasAlpha() == actualCCM.hasAlpha()
                    &&defCCM.isAlphaPremultiplied() == actualCCM.isAlphaPremultiplied()//&& colorSpaceIsOk
                    && defCCM.getTransparency() == actualCCM.getTransparency()
                    && defCCM.getTransferType() == actualCCM.getTransferType()
                    && defCCM.getPixelSize() == actualCCM.getPixelSize());
           
        }
   
        //
        //
        // IndexColorModel
        //
        //
   
        if (defaultCM instanceof IndexColorModel && actualCM instanceof IndexColorModel) {
            final IndexColorModel defICM = (IndexColorModel) defaultCM, actualICM = (IndexColorModel) actualCM;
            if (defICM.getNumColorComponents() != actualICM.getNumColorComponents()
                    || defICM.hasAlpha() != actualICM.hasAlpha()
                    || !defICM.getColorSpace().equals(actualICM.getColorSpace())
                    || defICM.getTransferType() != actualICM.getTransferType())
                return true;
   
            //
            // Suggesting expansion in the simplest case
            //
            if (defICM.getMapSize() != actualICM.getMapSize()
                    || defICM.getTransparency() != actualICM.getTransparency()
                    || defICM.getTransferType() != actualICM.getTransferType()
                    || defICM.getTransparentPixel() != actualICM.getTransparentPixel()) {
                configuration.setExpandToRGB(true);
                return false;
            }
   
            //
            // Now checking palettes to see if we need to do a color convert
            //
            // get the palette for this color model
            int numBands = actualICM.getNumColorComponents();
            byte[][] actualPalette = new byte[3][actualICM.getMapSize()];
            actualICM.getReds(actualPalette[0]);
            actualICM.getGreens(actualPalette[0]);
            actualICM.getBlues(actualPalette[0]);
            if (numBands == 4)
                actualICM.getAlphas(defaultPalette[0]);
            // compare them
            for (int i = 0; i < defICM.getMapSize(); i++)
                for (int j = 0; j < numBands; j++)
                    if (actualPalette[j][i] != defaultPalette[j][i]) {
                        configuration.setExpandToRGB(true);
                        break;
                    }
            return false;
   
        }
   
        //
        // if we get here this means that the two color models where completely
        // different, hence skip this feature.
        //
        return true;
    }
   
   
    /*
     * Checks if the provided factory spi builds a H2 store
     */
    public static boolean isH2Store(DataStoreFactorySpi spi) {
        String spiName = spi == null ? null : spi.getClass().getName();
        return "org.geotools.data.h2.H2DataStoreFactory".equals(spiName) ||
        "org.geotools.data.h2.H2JNDIDataStoreFactory".equals(spiName);
    }

    public static void fixH2DatabaseLocation(Map<String, Serializable> params, String parentLocation) throws MalformedURLException {
        if(params.containsKey(DATABASE_KEY)){
            String dbname = (String) params.get(DATABASE_KEY);
            // H2 database URLs must not be percent-encoded: see GEOT-4262.
            params.put(DATABASE_KEY,
                    "file:" + (new File(DataUtilities.urlToFile(new URL(parentLocation)),
                                    dbname)).getPath());
        }
    }

    /**
     * Checks if the provided factory spi builds a Oracle store
     */
    public static boolean isOracleStore(DataStoreFactorySpi spi) {
        String spiName = spi == null ? null : spi.getClass().getName();
        return "org.geotools.data.oracle.OracleNGOCIDataStoreFactory".equals(spiName) ||
                "org.geotools.data.oracle.OracleNGJNDIDataStoreFactory".equals(spiName) ||
                "org.geotools.data.oracle.OracleNGDataStoreFactory".equals(spiName);
    }

    /**
     * Checks if the provided factory spi builds a Postgis store
     */
    public static boolean isPostgisStore(DataStoreFactorySpi spi) {
        String spiName = spi == null ? null : spi.getClass().getName();
        return "org.geotools.data.postgis.PostgisNGJNDIDataStoreFactory".equals(spiName) ||
               "org.geotools.data.postgis.PostgisNGDataStoreFactory".equals(spiName);
    }

    /**
     * Merge statistics across datasets.
     *
     * @param pamDatasets
     * @return
     */
    public static PAMDataset mergePamDatasets (PAMDataset[] pamDatasets) {
        PAMDataset merged = pamDatasets[0];
        if (pamDatasets.length > 1) {
            merged = initRasterBands(pamDatasets[0]);
            if (merged != null){
                for (PAMDataset pamDataset: pamDatasets) {
                    updatePamDatasets(pamDataset, merged);
                }
            }
        }
        return merged;
    }

    /**
     * Merge basic statistics on destination {@link PAMDataset}
     * {@link PAMRasterBand}s need to have same size. No checks are performed here
     *
     * @param inputPamDataset
     * @param outputPamDataset
     */
    private static void updatePamDatasets(PAMDataset inputPamDataset, PAMDataset outputPamDataset) {
        List<PAMRasterBand> inputRasterBands = inputPamDataset.getPAMRasterBand();
        List<PAMRasterBand> outputRasterBands = outputPamDataset.getPAMRasterBand();
        for (int i = 0; i < inputRasterBands.size(); i++) {
            updateRasterBand(inputRasterBands.get(i), outputRasterBands.get(i));
        }
       
    }

    /**
     * Merge basic statistics on {@link PAMRasterBand} by updating min/max
     * Other statistics still need some work.
     * {@link MDI}s need to have same size. No checks are performed here
     *
     * @param inputPamRasterBand
     * @param outputPamRasterBand
     */
    private static void updateRasterBand(PAMRasterBand inputPamRasterBand, PAMRasterBand outputPamRasterBand) {
        List<MDI> mdiInputs = inputPamRasterBand.getMetadata().getMDI();
        List<MDI> mdiOutputs = outputPamRasterBand.getMetadata().getMDI();
        for (int i=0; i < mdiInputs.size(); i++) {
            MDI mdiInput = mdiInputs.get(i);
            MDI mdiOutput = mdiOutputs.get(i);
            updateMDI(mdiInput, mdiOutput);
        }
       
    }

    /**
     * Update min and max for mdiOutput.
     * Other statistics need better management.
     * For the moment we simply returns the min between them
     * @param mdiInput
     * @param mdiOutput
     */
    private static void updateMDI(MDI mdiInput, MDI mdiOutput) {
        Double current = Double.parseDouble(mdiInput.getValue());
        Object value = mdiOutput.getValue();
        if (value != null) {
            Double output = Double.parseDouble((String) value);
            if (mdiInput.getKey().toUpperCase().endsWith("_MAXIMUM")) {
                if (current < output) {
                    current = output;
                }
            } else {
                if (output < current) {
                    current = output;
                }
            }
        }
        mdiOutput.setValue(Double.toString(current));
    }

    /**
     * Initialize a list of {@link PAMRasterBand}s having same size of the
     * sample {@link PAMDataset} and same metadata names.
     * @param merged
     * @param samplePam
     * @return
     */
    private static PAMDataset initRasterBands(PAMDataset samplePam) {
        PAMDataset merged = null;
        if (samplePam != null) {
            merged = new PAMDataset();
            final List<PAMRasterBand> samplePamRasterBands = samplePam.getPAMRasterBand();
            final int numBands = samplePamRasterBands.size();
            List<PAMRasterBand> pamRasterBands = merged.getPAMRasterBand();
            PAMRasterBand sampleBand = samplePamRasterBands.get(0);
            List<MDI> sampleMetadata = sampleBand.getMetadata().getMDI();
            for (int i = 0; i<numBands; i++) {
                final PAMRasterBand band = new PAMRasterBand();
                final Metadata metadata = new Metadata();
                List<MDI> mdiList = metadata.getMDI();
                for (MDI mdi: sampleMetadata) {
                    MDI addedMdi = new MDI();
                    addedMdi.setKey(mdi.getKey());
                    mdiList.add(addedMdi);
                }
                band.setMetadata(metadata);
                pamRasterBands.add(band);
            }
        }
        return merged;
    }

    public static IOFileFilter getCleanupFilter() {
       return CLEANUP_FILTER;
    }

    public static void fixH2MVCCParam(Map<String, Serializable> params) {
        if (params != null) {
            // H2 database URLs must not be percent-encoded: see GEOT-4262.
            params.put(MVCC_KEY, true);
        }
    }

    public static void fixPostgisDBCreationParams(Map<String, Serializable> datastoreParams) {
        datastoreParams.put("create database", true);
    }

    public static void dropDB(DataStoreFactorySpi spi, Properties properties) throws IOException {
        if (Utils.isPostgisStore(spi)) {
            final Map params = filterDataStoreParams(properties, spi);
            // Use reflection to invoke dropDatabase on postGis factory DB
            try {
                MethodUtils.invokeMethod(spi, "dropDatabase", params);
            } catch (NoSuchMethodException e) {
                throw new IOException("Unable to drop the database: ", e);
            } catch (IllegalAccessException e) {
                throw new IOException("Unable to drop the database: ", e);
            } catch (InvocationTargetException e) {
                throw new IOException("Unable to drop the database: ", e);
            }
        }
    }

    public static ImageReaderSpi getReaderSpiFromStream(ImageReaderSpi suggestedSPI,
            ImageInputStream inStream) throws IOException {
        ImageReaderSpi readerSPI = null;
        // get a reader and try to cache the suggested SPI first
        inStream.mark();
        if (suggestedSPI != null && suggestedSPI.canDecodeInput(inStream)) {
            readerSPI = suggestedSPI;
            inStream.reset();
        } else {
            inStream.mark();
            ImageReader reader = ImageIOExt.getImageioReader(inStream);
            if (reader != null)
                readerSPI = reader.getOriginatingProvider();
            inStream.reset();
        }
        return readerSPI;
    }

    public static ImageInputStreamSpi getInputStreamSPIFromURL(URL granuleUrl) throws IOException {

        ImageInputStreamSpi streamSPI = ImageIOExt.getImageInputStreamSPI(granuleUrl, true);
        if (streamSPI == null) {
            final File file = DataUtilities.urlToFile(granuleUrl);
            if (file != null) {
                if (LOGGER.isLoggable(Level.WARNING)) {
                    LOGGER.log(Level.WARNING, Utils.getFileInfo(file));
                }
            }
            throw new IllegalArgumentException(
                    "Unable to get an input stream for the provided granule "
                            + granuleUrl.toString());
        }
        return streamSPI;
    }
}
TOP

Related Classes of org.geotools.gce.imagemosaic.Utils

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.