org.geotools.coverage.io.netcdf.NetCDFMosaicReaderTest.java Source code

Java tutorial

Introduction

Here is the source code for org.geotools.coverage.io.netcdf.NetCDFMosaicReaderTest.java

Source

/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2006-2008, Open Source Geospatial Foundation (OSGeo)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.coverage.io.netcdf;

import it.geosolutions.imageio.utilities.ImageIOUtilities;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import javax.media.jai.PlanarImage;
import javax.swing.JFrame;

import junit.framework.JUnit4TestAdapter;
import junit.textui.TestRunner;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GranuleSource;
import org.geotools.coverage.grid.io.GridFormatFinder;
import org.geotools.coverage.grid.io.HarvestedSource;
import org.geotools.data.DataUtilities;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.Hints;
import org.geotools.gce.imagemosaic.ImageMosaicFormat;
import org.geotools.gce.imagemosaic.ImageMosaicReader;
import org.geotools.gce.imagemosaic.Utils.Prop;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.resources.image.ImageUtilities;
import org.geotools.test.TestData;
import org.geotools.util.logging.Logging;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.sort.SortBy;
import org.opengis.filter.sort.SortOrder;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterValue;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;

/**
 * Testing {@link ImageMosaicReader}.
 * 
 * @author Simone Giannecchini, GeoSolutions
 * @author Stefan Alfons Krueger (alfonx), Wikisquare.de
 * @since 2.3
 * 
 * 
 * 
 * @source $URL$
 */
public class NetCDFMosaicReaderTest extends Assert {

    private final static Logger LOGGER = Logging.getLogger(NetCDFMosaicReaderTest.class.toString());

    public static junit.framework.Test suite() {
        return new JUnit4TestAdapter(NetCDFMosaicReaderTest.class);
    }

    private static boolean INTERACTIVE;

    /**
     * Simple test method accessing time and 2 custom dimensions for the sample dataset
     * 
     * @throws IOException
     * @throws FactoryException
     * @throws NoSuchAuthorityCodeException
     * @throws ParseException +
     */
    @Test
    @Ignore
    @SuppressWarnings("rawtypes")
    public void timeAdditionalDimRanges() throws Exception {
        final String dlrFolder = "/work/data/DLR/samplesForMosaic";
        final File file = new File(dlrFolder);
        final URL url = DataUtilities.fileToURL(file);
        final Hints hints = new Hints(Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM, CRS.decode("EPSG:4326", true));

        // Get format
        final AbstractGridFormat format = (AbstractGridFormat) GridFormatFinder.findFormat(url, hints);
        final ImageMosaicReader reader = getReader(url, format);
        String[] names = reader.getGridCoverageNames();

        for (String name : names) {
            LOGGER.info("Coverage: " + name);
            final String[] metadataNames = reader.getMetadataNames(name);
            assertNotNull(metadataNames);
            assertEquals(metadataNames.length, 12);
            assertEquals("true", reader.getMetadataValue(name, "HAS_TIME_DOMAIN"));
            assertEquals(
                    "2012-04-01T00:00:00.000Z,2012-04-01T01:00:00.000Z,2012-04-01T02:00:00.000Z,2012-04-01T03:00:00.000Z,2012-04-01T04:00:00.000Z,2012-04-01T05:00:00.000Z,2012-04-01T06:00:00.000Z,2012-04-01T07:00:00.000Z,2012-04-01T08:00:00.000Z,2012-04-01T09:00:00.000Z,2012-04-01T10:00:00.000Z,2012-04-01T11:00:00.000Z,2012-04-01T12:00:00.000Z,2012-04-01T13:00:00.000Z,2012-04-01T14:00:00.000Z,2012-04-01T15:00:00.000Z,2012-04-01T16:00:00.000Z,2012-04-01T17:00:00.000Z,2012-04-01T18:00:00.000Z,2012-04-01T19:00:00.000Z,2012-04-01T20:00:00.000Z,2012-04-01T21:00:00.000Z,2012-04-01T22:00:00.000Z,2012-04-01T23:00:00.000Z",
                    reader.getMetadataValue(name, "TIME_DOMAIN"));
            assertEquals("2012-04-01T00:00:00.000Z", reader.getMetadataValue(name, "TIME_DOMAIN_MINIMUM"));
            assertEquals("2012-04-01T23:00:00.000Z", reader.getMetadataValue(name, "TIME_DOMAIN_MAXIMUM"));

            assertEquals("true", reader.getMetadataValue(name, "HAS_ELEVATION_DOMAIN"));
            assertEquals("10.0,35.0,75.0,125.0,175.0,250.0,350.0,450.0,550.0,700.0,900.0,1250.0,1750.0,2500.0",
                    reader.getMetadataValue(name, "ELEVATION_DOMAIN"));
            assertEquals("10.0", reader.getMetadataValue(name, "ELEVATION_DOMAIN_MINIMUM"));
            assertEquals("2500.0", reader.getMetadataValue(name, "ELEVATION_DOMAIN_MAXIMUM"));

            assertEquals("true", reader.getMetadataValue(name, "HAS_RUNTIME_DOMAIN"));
            assertEquals("2012-05-09T12:29:30.000Z,2013-03-30T16:15:58.648Z",
                    reader.getMetadataValue(name, "RUNTIME_DOMAIN"));
            assertEquals("2012-05-09T12:29:30.000Z", reader.getMetadataValue(name, "RUNTIME_DOMAIN_MINIMUM"));
            assertEquals("2013-03-30T16:15:58.648Z", reader.getMetadataValue(name, "RUNTIME_DOMAIN_MAXIMUM"));

            // use imageio with defined tiles
            final ParameterValue<Boolean> useJai = AbstractGridFormat.USE_JAI_IMAGEREAD.createValue();
            useJai.setValue(false);

            // specify time
            final ParameterValue<List> time = ImageMosaicFormat.TIME.createValue();
            final Date timeD = parseTimeStamp("2012-04-01T00:00:00.000Z");
            time.setValue(new ArrayList() {
                {
                    add(timeD);
                }
            });

            final ParameterValue<List> elevation = ImageMosaicFormat.ELEVATION.createValue();
            elevation.setValue(new ArrayList() {
                {
                    add(75d); // Elevation
                }
            });

            Set<ParameterDescriptor<List>> params = reader.getDynamicParameters(name);
            ParameterValue<List<String>> runtime = null;
            final String selectedWaveLength = "2013-03-30T16:15:58.648Z";
            for (ParameterDescriptor param : params) {
                if (param.getName().getCode().equalsIgnoreCase("RUNTIME")) {
                    runtime = param.createValue();
                    runtime.setValue(new ArrayList<String>() {
                        {
                            add(selectedWaveLength);
                        }
                    });
                }
            }
            assertNotNull(runtime);

            // Test the output coverage
            GeneralParameterValue[] values = new GeneralParameterValue[] { useJai, time, elevation, runtime };
            final GridCoverage2D coverage = (GridCoverage2D) reader.read(name, values);
            Assert.assertNotNull(coverage);
            final String fileSource = (String) coverage
                    .getProperty(AbstractGridCoverage2DReader.FILE_SOURCE_PROPERTY);

            // Check the proper granule has been read
            final String baseName = FilenameUtils.getBaseName(fileSource);
            assertEquals(baseName, "20130102polyphemus");
        }
    }

    @Test
    public void testHarvestAddTime() throws IOException {
        // prepare a "mosaic" with just one NetCDF
        File nc1 = TestData.file(this, "polyphemus_20130301_test.nc");
        File mosaic = new File(TestData.file(this, "."), "nc_harvest1");
        if (mosaic.exists()) {
            FileUtils.deleteDirectory(mosaic);
        }
        assertTrue(mosaic.mkdirs());
        FileUtils.copyFileToDirectory(nc1, mosaic);

        // The indexer
        String indexer = "TimeAttribute=time\n"
                + "Schema=the_geom:Polygon,location:String,imageindex:Integer,time:java.util.Date\n";
        FileUtils.writeStringToFile(new File(mosaic, "indexer.properties"), indexer);

        // the datastore.properties file is also mandatory...
        File dsp = TestData.file(this, "datastore.properties");
        FileUtils.copyFileToDirectory(dsp, mosaic);

        // have the reader harvest it
        ImageMosaicFormat format = new ImageMosaicFormat();
        ImageMosaicReader reader = format.getReader(mosaic);
        SimpleFeatureIterator it = null;
        assertNotNull(reader);
        try {
            String[] names = reader.getGridCoverageNames();
            assertEquals(1, names.length);
            assertEquals("O3", names[0]);

            // check we have the two granules we expect
            GranuleSource source = reader.getGranules("O3", true);
            FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
            Query q = new Query(Query.ALL);
            q.setSortBy(new SortBy[] { ff.sort("time", SortOrder.ASCENDING) });
            SimpleFeatureCollection granules = source.getGranules(q);
            assertEquals(2, granules.size());
            it = granules.features();
            assertTrue(it.hasNext());
            SimpleFeature f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(0, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T00:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            assertTrue(it.hasNext());
            f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(1, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T01:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            it.close();

            // now add another netcdf and harvest it
            File nc2 = TestData.file(this, "polyphemus_20130302_test.nc");
            FileUtils.copyFileToDirectory(nc2, mosaic);
            File fileToHarvest = new File(mosaic, "polyphemus_20130302_test.nc");
            List<HarvestedSource> harvestSummary = reader.harvest(null, fileToHarvest, null);
            assertEquals(1, harvestSummary.size());
            HarvestedSource hf = harvestSummary.get(0);
            assertEquals("polyphemus_20130302_test.nc", ((File) hf.getSource()).getName());
            assertTrue(hf.success());
            assertEquals(1, reader.getGridCoverageNames().length);

            // check that we have four times now
            granules = source.getGranules(q);
            assertEquals(4, granules.size());
            it = granules.features();
            f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(0, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T00:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            assertTrue(it.hasNext());
            f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(1, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T01:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            f = it.next();
            assertEquals("polyphemus_20130302_test.nc", f.getAttribute("location"));
            assertEquals(0, f.getAttribute("imageindex"));
            assertEquals("2013-03-02T00:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            assertTrue(it.hasNext());
            f = it.next();
            assertEquals("polyphemus_20130302_test.nc", f.getAttribute("location"));
            assertEquals(1, f.getAttribute("imageindex"));
            assertEquals("2013-03-02T01:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            it.close();
        } finally {
            if (it != null) {
                it.close();
            }
            reader.dispose();
        }
    }

    @Test
    public void testReHarvest() throws Exception {
        // prepare a "mosaic" with just one NetCDF
        File nc1 = TestData.file(this, "polyphemus_20130301_test.nc");
        File mosaic = new File(TestData.file(this, "."), "nc_harvest4");
        if (mosaic.exists()) {
            FileUtils.deleteDirectory(mosaic);
        }
        assertTrue(mosaic.mkdirs());
        FileUtils.copyFileToDirectory(nc1, mosaic);

        // The indexer
        String indexer = "TimeAttribute=time\n"
                + "Schema=the_geom:Polygon,location:String,imageindex:Integer,time:java.util.Date\n";
        FileUtils.writeStringToFile(new File(mosaic, "indexer.properties"), indexer);

        // the datastore.properties file is also mandatory...
        File dsp = TestData.file(this, "datastore.properties");
        FileUtils.copyFileToDirectory(dsp, mosaic);

        // have the reader harvest it
        ImageMosaicFormat format = new ImageMosaicFormat();
        ImageMosaicReader reader = format.getReader(mosaic);
        SimpleFeatureIterator it = null;
        assertNotNull(reader);
        try {
            String[] names = reader.getGridCoverageNames();
            assertEquals(1, names.length);
            assertEquals("O3", names[0]);

            // check we have the two granules we expect
            GranuleSource source = reader.getGranules("O3", true);
            FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
            Query q = new Query(Query.ALL);
            q.setSortBy(new SortBy[] { ff.sort("time", SortOrder.ASCENDING) });
            SimpleFeatureCollection granules = source.getGranules(q);
            assertEquals(2, granules.size());
            it = granules.features();
            assertTrue(it.hasNext());
            SimpleFeature f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(0, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T00:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            assertTrue(it.hasNext());
            f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(1, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T01:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            it.close();

            // close the reader and re-open it
            reader.dispose();
            reader = format.getReader(mosaic);
            source = reader.getGranules("O3", true);

            // wait a bit, we have to make sure the old indexes are recognized as old
            Thread.sleep(1000);

            // now replace the netcdf file with a more up to date version of the same 
            File nc2 = TestData.file(this, "polyphemus_20130301_test_more_times.nc");
            File target = new File(mosaic, "polyphemus_20130301_test.nc");
            FileUtils.copyFile(nc2, target, false);
            File fileToHarvest = new File(mosaic, "polyphemus_20130301_test.nc");
            List<HarvestedSource> harvestSummary = reader.harvest(null, fileToHarvest, null);
            assertEquals(1, harvestSummary.size());
            HarvestedSource hf = harvestSummary.get(0);
            assertEquals("polyphemus_20130301_test.nc", ((File) hf.getSource()).getName());
            assertTrue(hf.success());
            assertEquals(1, reader.getGridCoverageNames().length);

            // check that we have four times now
            source = reader.getGranules("O3", true);
            granules = source.getGranules(q);
            assertEquals(4, granules.size());
            it = granules.features();
            f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(0, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T00:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            assertTrue(it.hasNext());
            f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(1, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T01:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(2, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T02:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            assertTrue(it.hasNext());
            f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(3, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T03:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            it.close();
        } finally {
            if (it != null) {
                it.close();
            }
            reader.dispose();
        }
    }

    @Test
    public void testHarvestHDF5Data() throws IOException {
        File nc1 = TestData.file(this, "2DLatLonCoverage.nc");
        File nc2 = TestData.file(this, "2DLatLonCoverage2.nc");
        File mosaic = new File(TestData.file(this, "."), "simpleMosaic");
        if (mosaic.exists()) {
            FileUtils.deleteDirectory(mosaic);
        }
        assertTrue(mosaic.mkdirs());
        FileUtils.copyFileToDirectory(nc1, mosaic);
        FileUtils.copyFileToDirectory(nc2, mosaic);

        // the datastore.properties file is also mandatory...
        File dsp = TestData.file(this, "datastore.properties");
        FileUtils.copyFileToDirectory(dsp, mosaic);

        File xml = TestData.file(this, "hdf5Coverage2D.xml");
        FileUtils.copyFileToDirectory(xml, mosaic);

        // The indexer
        String indexer = "TimeAttribute=time\n"
                + "Schema=the_geom:Polygon,location:String,imageindex:Integer,time:java.util.Date\n";
        //  + "PropertyCollectors=TimestampFileNameExtractorSPI[timeregex](time)\n";
        indexer += Prop.AUXILIARY_FILE + "=" + "hdf5Coverage2D.xml";
        FileUtils.writeStringToFile(new File(mosaic, "indexer.properties"), indexer);

        //simply test if the mosaic can be read without exceptions
        ImageMosaicFormat format = new ImageMosaicFormat();
        ImageMosaicReader reader = format.getReader(mosaic);
        reader.read("L1_V2", null);
    }

    @Test
    public void testHarvestAddVariable() throws IOException {
        // prepare a "mosaic" with just one NetCDF
        File nc1 = TestData.file(this, "polyphemus_20130301_test.nc");
        File mosaic = new File(TestData.file(this, "."), "nc_harvest2");
        if (mosaic.exists()) {
            FileUtils.deleteDirectory(mosaic);
        }
        assertTrue(mosaic.mkdirs());
        FileUtils.copyFileToDirectory(nc1, mosaic);

        // The indexer
        String indexer = "TimeAttribute=time\n"
                + "Schema=the_geom:Polygon,location:String,imageindex:Integer,time:java.util.Date\n";
        FileUtils.writeStringToFile(new File(mosaic, "indexer.properties"), indexer);

        // the datastore.properties file is also mandatory...
        File dsp = TestData.file(this, "datastore.properties");
        FileUtils.copyFileToDirectory(dsp, mosaic);

        // have the reader harvest it
        ImageMosaicFormat format = new ImageMosaicFormat();
        ImageMosaicReader reader = format.getReader(mosaic);
        SimpleFeatureIterator it = null;
        assertNotNull(reader);
        try {
            String[] names = reader.getGridCoverageNames();
            assertEquals(1, names.length);
            assertEquals("O3", names[0]);

            // check we have the two granules we expect
            GranuleSource source = reader.getGranules("O3", true);
            FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
            Query q = new Query(Query.ALL);
            q.setSortBy(new SortBy[] { ff.sort("time", SortOrder.ASCENDING) });
            SimpleFeatureCollection granules = source.getGranules(q);
            assertEquals(2, granules.size());
            it = granules.features();
            assertTrue(it.hasNext());
            SimpleFeature f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(0, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T00:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            assertTrue(it.hasNext());
            f = it.next();
            assertEquals("polyphemus_20130301_test.nc", f.getAttribute("location"));
            assertEquals(1, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T01:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            it.close();

            // now add another netcdf and harvest it
            File nc2 = TestData.file(this, "polyphemus_20130301_NO2.nc");
            FileUtils.copyFileToDirectory(nc2, mosaic);
            File fileToHarvest = new File(mosaic, "polyphemus_20130301_NO2.nc");
            List<HarvestedSource> harvestSummary = reader.harvest(null, fileToHarvest, null);
            assertEquals(1, harvestSummary.size());
            HarvestedSource hf = harvestSummary.get(0);
            assertEquals("polyphemus_20130301_NO2.nc", ((File) hf.getSource()).getName());
            assertTrue(hf.success());
            // check we have two coverages now
            names = reader.getGridCoverageNames();
            Arrays.sort(names);
            assertEquals(2, names.length);
            assertEquals("NO2", names[0]);
            assertEquals("O3", names[1]);

            // test the newly ingested granules, which are in a separate coverage
            q.setTypeName("NO2");
            granules = source.getGranules(q);
            assertEquals(2, granules.size());
            it = granules.features();
            f = it.next();
            assertEquals("polyphemus_20130301_NO2.nc", f.getAttribute("location"));
            assertEquals(0, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T00:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            assertTrue(it.hasNext());
            f = it.next();
            assertEquals("polyphemus_20130301_NO2.nc", f.getAttribute("location"));
            assertEquals(1, f.getAttribute("imageindex"));
            assertEquals("2013-03-01T01:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            it.close();
        } finally {
            if (it != null) {
                it.close();
            }
            reader.dispose();
        }
    }

    @Test
    public void testHarvest3Gome() throws IOException {
        // prepare a "mosaic" with just one NetCDF
        File nc1 = TestData.file(this, "20130101.METOPA.GOME2.NO2.DUMMY.nc");
        File mosaic = new File(TestData.file(this, "."), "nc_harvest");
        if (mosaic.exists()) {
            FileUtils.deleteDirectory(mosaic);
        }
        assertTrue(mosaic.mkdirs());
        FileUtils.copyFileToDirectory(nc1, mosaic);

        File xml = TestData.file(this, ".DUMMY.GOME2.NO2.PGL/GOME2.NO2.xml");
        FileUtils.copyFileToDirectory(xml, mosaic);

        // The indexer
        String indexer = "TimeAttribute=time\n"
                + "Schema=the_geom:Polygon,location:String,imageindex:Integer,time:java.util.Date\n"
                + "PropertyCollectors=TimestampFileNameExtractorSPI[timeregex](time)\n";
        indexer += Prop.AUXILIARY_FILE + "=" + "GOME2.NO2.xml";
        FileUtils.writeStringToFile(new File(mosaic, "indexer.properties"), indexer);

        String timeregex = "regex=[0-9]{8}";
        FileUtils.writeStringToFile(new File(mosaic, "timeregex.properties"), timeregex);

        // the datastore.properties file is also mandatory...
        File dsp = TestData.file(this, "datastore.properties");
        FileUtils.copyFileToDirectory(dsp, mosaic);

        // have the reader harvest it
        ImageMosaicFormat format = new ImageMosaicFormat();
        ImageMosaicReader reader = format.getReader(mosaic);
        SimpleFeatureIterator it = null;
        assertNotNull(reader);
        try {
            String[] names = reader.getGridCoverageNames();
            assertEquals(1, names.length);
            assertEquals("NO2", names[0]);

            // check we have the two granules we expect
            GranuleSource source = reader.getGranules("NO2", true);
            FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
            Query q = new Query(Query.ALL);
            q.setSortBy(new SortBy[] { ff.sort("time", SortOrder.DESCENDING) });
            SimpleFeatureCollection granules = source.getGranules(q);
            assertEquals(1, granules.size());
            it = granules.features();
            assertTrue(it.hasNext());
            SimpleFeature f = it.next();
            assertEquals("20130101.METOPA.GOME2.NO2.DUMMY.nc", f.getAttribute("location"));
            assertEquals(0, f.getAttribute("imageindex"));
            assertEquals("2013-01-01T00:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            it.close();

            // now add another netcdf and harvest it
            File nc2 = TestData.file(this, "20130116.METOPA.GOME2.NO2.DUMMY.nc");
            FileUtils.copyFileToDirectory(nc2, mosaic);
            File fileToHarvest = new File(mosaic, "20130116.METOPA.GOME2.NO2.DUMMY.nc");
            List<HarvestedSource> harvestSummary = reader.harvest("NO2", fileToHarvest, null);
            assertEquals(1, harvestSummary.size());
            granules = source.getGranules(q);
            assertEquals(2, granules.size());

            HarvestedSource hf = harvestSummary.get(0);
            assertEquals("20130116.METOPA.GOME2.NO2.DUMMY.nc", ((File) hf.getSource()).getName());
            assertTrue(hf.success());
            assertEquals(1, reader.getGridCoverageNames().length);

            File nc3 = TestData.file(this, "20130108.METOPA.GOME2.NO2.DUMMY.nc");
            FileUtils.copyFileToDirectory(nc3, mosaic);
            fileToHarvest = new File(mosaic, "20130108.METOPA.GOME2.NO2.DUMMY.nc");
            harvestSummary = reader.harvest("NO2", fileToHarvest, null);
            assertEquals(1, harvestSummary.size());
            hf = harvestSummary.get(0);
            assertEquals("20130108.METOPA.GOME2.NO2.DUMMY.nc", ((File) hf.getSource()).getName());
            assertTrue(hf.success());
            assertEquals(1, reader.getGridCoverageNames().length);

            // check that we have 2 times now
            granules = source.getGranules(q);
            assertEquals(3, granules.size());
            it = granules.features();
            f = it.next();
            assertEquals("20130116.METOPA.GOME2.NO2.DUMMY.nc", f.getAttribute("location"));
            assertEquals(0, f.getAttribute("imageindex"));
            assertEquals("2013-01-16T00:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            assertTrue(it.hasNext());
            f = it.next();
            assertEquals("20130108.METOPA.GOME2.NO2.DUMMY.nc", f.getAttribute("location"));
            assertEquals(0, f.getAttribute("imageindex"));
            assertEquals("2013-01-08T00:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));
            f = it.next();
            assertEquals("20130101.METOPA.GOME2.NO2.DUMMY.nc", f.getAttribute("location"));
            assertEquals(0, f.getAttribute("imageindex"));
            assertEquals("2013-01-01T00:00:00.000Z", ConvertersHack.convert(f.getAttribute("time"), String.class));

            it.close();
        } finally {
            if (it != null) {
                it.close();
            }
            reader.dispose();
        }
    }

    @Test
    public void testReadCoverageGome() throws IOException {
        // prepare a "mosaic" with just one NetCDF
        File nc1 = TestData.file(this, "20130101.METOPA.GOME2.NO2.DUMMY.nc");
        File mosaic = new File(TestData.file(this, "."), "nc_harvest3");
        if (mosaic.exists()) {
            FileUtils.deleteDirectory(mosaic);
        }
        assertTrue(mosaic.mkdirs());
        FileUtils.copyFileToDirectory(nc1, mosaic);

        File xml = TestData.file(this, ".DUMMY.GOME2.NO2.PGL/GOME2.NO2.xml");
        FileUtils.copyFileToDirectory(xml, mosaic);

        // The indexer
        String indexer = "TimeAttribute=time\n"
                + "Schema=the_geom:Polygon,location:String,imageindex:Integer,time:java.util.Date\n"
                + "PropertyCollectors=TimestampFileNameExtractorSPI[timeregex](time)\n";
        indexer += Prop.AUXILIARY_FILE + "=" + "GOME2.NO2.xml";
        FileUtils.writeStringToFile(new File(mosaic, "indexer.properties"), indexer);

        String timeregex = "regex=[0-9]{8}";
        FileUtils.writeStringToFile(new File(mosaic, "timeregex.properties"), timeregex);

        // the datastore.properties file is also mandatory...
        File dsp = TestData.file(this, "datastore.properties");
        FileUtils.copyFileToDirectory(dsp, mosaic);

        // have the reader harvest it
        ImageMosaicFormat format = new ImageMosaicFormat();
        ImageMosaicReader reader = format.getReader(mosaic);
        GridCoverage2D coverage = null;
        assertNotNull(reader);
        try {
            String[] names = reader.getGridCoverageNames();
            assertEquals(1, names.length);
            assertEquals("NO2", names[0]);

            GranuleSource source = reader.getGranules("NO2", true);
            SimpleFeatureCollection granules = source.getGranules(Query.ALL);
            assertEquals(1, granules.size());

            assertTrue(CRS.equalsIgnoreMetadata(DefaultGeographicCRS.WGS84, reader.getCoordinateReferenceSystem()));
            GeneralEnvelope envelope = reader.getOriginalEnvelope("NO2");
            assertEquals(-360, envelope.getMinimum(0), 0d);
            assertEquals(360, envelope.getMaximum(0), 0d);
            assertEquals(-180, envelope.getMinimum(1), 0d);
            assertEquals(180, envelope.getMaximum(1), 0d);

            // check we can read a coverage out of it
            coverage = reader.read(null);
            reader.dispose();

            // Checking we can read again from the coverage once it has been configured.
            reader = format.getReader(mosaic);
            coverage = reader.read(null);
            assertNotNull(coverage);

        } finally {
            if (coverage != null) {
                ImageUtilities.disposePlanarImageChain((PlanarImage) coverage.getRenderedImage());
                coverage.dispose(true);
            }
            reader.dispose();
        }
    }

    @Test
    public void testDeleteCoverageGome() throws IOException {
        // prepare a "mosaic" with just one NetCDF
        File nc1 = TestData.file(this, "O3-NO2.nc");
        File mosaic = new File(TestData.file(this, "."), "nc_deleteCoverage");
        if (mosaic.exists()) {
            FileUtils.deleteDirectory(mosaic);
        }
        assertTrue(mosaic.mkdirs());
        FileUtils.copyFileToDirectory(nc1, mosaic);

        File xml = TestData.file(this, ".O3-NO2/O3-NO2.xml");
        FileUtils.copyFileToDirectory(xml, mosaic);

        // The indexer
        String indexer = "TimeAttribute=time\n"
                + "Schema=the_geom:Polygon,location:String,imageindex:Integer,time:java.util.Date\n";
        indexer += Prop.AUXILIARY_FILE + "=" + "O3-NO2.xml";
        FileUtils.writeStringToFile(new File(mosaic, "indexer.properties"), indexer);

        // the datastore.properties file is also mandatory...
        File dsp = TestData.file(this, "datastore.properties");
        FileUtils.copyFileToDirectory(dsp, mosaic);

        // have the reader harvest it
        ImageMosaicFormat format = new ImageMosaicFormat();
        ImageMosaicReader reader = format.getReader(mosaic);
        GridCoverage2D coverage = null;
        assertNotNull(reader);
        try {
            assertEquals(2, reader.getGridCoverageNames().length);

            File[] files = mosaic.listFiles();
            assertEquals(15, files.length);

            reader.dispose();
            reader = format.getReader(mosaic);

            reader.delete(false);
            files = mosaic.listFiles();
            assertEquals(2, files.length);

        } finally {
            if (coverage != null) {
                ImageUtilities.disposePlanarImageChain((PlanarImage) coverage.getRenderedImage());
                coverage.dispose(true);
            }
            reader.dispose();
        }
    }

    @Test
    public void testReadCoverageGome2Names() throws IOException {
        // prepare a "mosaic" with just one NetCDF
        File nc1 = TestData.file(this, "20130101.METOPA.GOME2.NO2.DUMMY.nc");
        File mosaic = new File(TestData.file(this, "."), "nc_gome2");
        if (mosaic.exists()) {
            FileUtils.deleteDirectory(mosaic);
        }
        assertTrue(mosaic.mkdirs());
        FileUtils.copyFileToDirectory(nc1, mosaic);

        nc1 = TestData.file(this, "20130101.METOPA.GOME2.BrO.DUMMY.nc");
        FileUtils.copyFileToDirectory(nc1, mosaic);

        File xml = TestData.file(this, "DUMMYGOME2.xml");
        FileUtils.copyFileToDirectory(xml, mosaic);

        // The indexer
        String indexer = "TimeAttribute=time\n"
                + "Schema=the_geom:Polygon,location:String,imageindex:Integer,time:java.util.Date\n"
                + "PropertyCollectors=TimestampFileNameExtractorSPI[timeregex](time)\n";
        indexer += Prop.AUXILIARY_FILE + "=" + "DUMMYGOME2.xml";
        FileUtils.writeStringToFile(new File(mosaic, "indexer.properties"), indexer);

        String timeregex = "regex=[0-9]{8}";
        FileUtils.writeStringToFile(new File(mosaic, "timeregex.properties"), timeregex);

        // the datastore.properties file is also mandatory...
        File dsp = TestData.file(this, "datastore.properties");
        FileUtils.copyFileToDirectory(dsp, mosaic);

        // have the reader harvest it
        ImageMosaicFormat format = new ImageMosaicFormat();
        ImageMosaicReader reader = format.getReader(mosaic);
        GridCoverage2D coverage = null;
        assertNotNull(reader);
        try {
            String[] names = reader.getGridCoverageNames();
            assertEquals(2, names.length);
            assertEquals("NO2", names[0]);
            assertEquals("BrO", names[1]);

            GranuleSource source = reader.getGranules("NO2", true);
            SimpleFeatureCollection granules = source.getGranules(Query.ALL);
            assertEquals(1, granules.size());

            assertTrue(CRS.equalsIgnoreMetadata(DefaultGeographicCRS.WGS84,
                    reader.getCoordinateReferenceSystem("NO2")));
            GeneralEnvelope envelope = reader.getOriginalEnvelope("NO2");
            assertEquals(-360, envelope.getMinimum(0), 0d);
            assertEquals(360, envelope.getMaximum(0), 0d);
            assertEquals(-180, envelope.getMinimum(1), 0d);
            assertEquals(180, envelope.getMaximum(1), 0d);

            // check we can read a coverage out of it
            coverage = reader.read("NO2", null);
            reader.dispose();

            // Checking we can read again from the coverage (using a different name this time) once it has been configured.
            reader = format.getReader(mosaic);
            coverage = reader.read("BrO", null);
            assertNotNull(coverage);

        } finally {
            if (coverage != null) {
                ImageUtilities.disposePlanarImageChain((PlanarImage) coverage.getRenderedImage());
                coverage.dispose(true);
            }
            reader.dispose();
        }
    }

    @Test
    public void testCheckDifferentSampleImages() throws IOException {
        // prepare a "mosaic" with just one NetCDF
        File nc1 = TestData.file(this, "20130101.METOPA.GOME2.NO2.DUMMY.nc");
        File mosaic = new File(TestData.file(this, "."), "nc_sampleimages");
        if (mosaic.exists()) {
            FileUtils.deleteDirectory(mosaic);
        }
        assertTrue(mosaic.mkdirs());
        FileUtils.copyFileToDirectory(nc1, mosaic);

        nc1 = TestData.file(this, "20130101.METOPA.GOME2.BrO.DUMMY.nc");
        FileUtils.copyFileToDirectory(nc1, mosaic);

        File xml = TestData.file(this, "DUMMYGOME2.xml");
        FileUtils.copyFileToDirectory(xml, mosaic);

        // The indexer
        String indexer = "TimeAttribute=time\n"
                + "Schema=the_geom:Polygon,location:String,imageindex:Integer,time:java.util.Date\n"
                + "PropertyCollectors=TimestampFileNameExtractorSPI[timeregex](time)\n";
        indexer += Prop.AUXILIARY_FILE + "=" + "DUMMYGOME2.xml";
        FileUtils.writeStringToFile(new File(mosaic, "indexer.properties"), indexer);

        String timeregex = "regex=[0-9]{8}";
        FileUtils.writeStringToFile(new File(mosaic, "timeregex.properties"), timeregex);

        // the datastore.properties file is also mandatory...
        File dsp = TestData.file(this, "datastore.properties");
        FileUtils.copyFileToDirectory(dsp, mosaic);

        // have the reader harvest it
        ImageMosaicFormat format = new ImageMosaicFormat();
        ImageMosaicReader reader = format.getReader(mosaic);
        assertNotNull(reader);

        // Checking whether different sample images have been created
        final File sampleImage1 = new File(TestData.file(this, "."), "nc_sampleimages/BrOsample_image");
        final File sampleImage2 = new File(TestData.file(this, "."), "nc_sampleimages/NO2sample_image");
        assertTrue(sampleImage1.exists());
        assertTrue(sampleImage2.exists());
        reader.dispose();
    }

    @Test
    @Ignore
    public void oracle() throws IOException, ParseException, NoSuchAuthorityCodeException, FactoryException {
        final File workDir = new File("C:\\data\\dlr\\ascatL1_mosaic");

        final AbstractGridFormat format = new ImageMosaicFormat();
        assertNotNull(format);
        ImageMosaicReader reader = (ImageMosaicReader) format.getReader(workDir.toURI().toURL());
        assertNotNull(format);
        String[] names = reader.getGridCoverageNames();
        String name = names[1];

        final String[] metadataNames = reader.getMetadataNames(name);
        assertNotNull(metadataNames);
        assertEquals(metadataNames.length, 18);

        assertEquals("false", reader.getMetadataValue(name, "HAS_TIME_DOMAIN"));

        assertEquals("true", reader.getMetadataValue(name, "HAS_NUMSIGMA_DOMAIN"));
        assertEquals("0,1,2", reader.getMetadataValue(name, "NUMSIGMA_DOMAIN"));
        assertEquals("java.lang.Integer", reader.getMetadataValue(name, "NUMSIGMA_DOMAIN_DATATYPE"));

        assertEquals("true", reader.getMetadataValue(name, "HAS_RUNTIME_DOMAIN"));
        assertEquals("false", reader.getMetadataValue(name, "HAS_ELEVATION_DOMAIN"));
        assertEquals("false", reader.getMetadataValue(name, "HAS_XX_DOMAIN"));
        assertEquals("20110620020000", reader.getMetadataValue(name, "RUNTIME_DOMAIN"));
        assertEquals("java.lang.String", reader.getMetadataValue(name, "RUNTIME_DOMAIN_DATATYPE"));

        // limit yourself to reading just a bit of it
        final ParameterValue<GridGeometry2D> gg = AbstractGridFormat.READ_GRIDGEOMETRY2D.createValue();
        final GeneralEnvelope envelope = reader.getOriginalEnvelope(name);
        final Dimension dim = new Dimension();
        dim.setSize(reader.getOriginalGridRange(name).getSpan(0) / 2.0,
                reader.getOriginalGridRange(name).getSpan(1) / 2.0);
        final Rectangle rasterArea = ((GridEnvelope2D) reader.getOriginalGridRange(name));
        rasterArea.setSize(dim);
        final GridEnvelope2D range = new GridEnvelope2D(rasterArea);
        gg.setValue(new GridGeometry2D(range, envelope));

        final ParameterValue<Boolean> direct = ImageMosaicFormat.USE_JAI_IMAGEREAD.createValue();
        direct.setValue(false);

        final ParameterValue<double[]> bkg = ImageMosaicFormat.BACKGROUND_VALUES.createValue();
        bkg.setValue(new double[] { -9999.0 });

        ParameterValue<List<String>> dateValue = null;
        ParameterValue<List<String>> sigmaValue = null;
        final String selectedSigma = "1";
        final String selectedRuntime = "20110620020000";
        Set<ParameterDescriptor<List>> params = reader.getDynamicParameters(name);
        for (ParameterDescriptor param : params) {
            if (param.getName().getCode().equalsIgnoreCase("RUNTIME")) {
                dateValue = param.createValue();
                dateValue.setValue(new ArrayList<String>() {
                    {
                        add(selectedRuntime);
                    }
                });
            } else if (param.getName().getCode().equalsIgnoreCase("NUMSIGMA")) {
                sigmaValue = param.createValue();
                sigmaValue.setValue(new ArrayList<String>() {
                    {
                        add(selectedSigma);
                    }
                });
            }
        }
        // Test the output coverage
        GridCoverage2D coverage = reader.read(name,
                new GeneralParameterValue[] { gg, bkg, direct, sigmaValue, dateValue });
        assertNotNull(coverage);

    }

    private Date parseTimeStamp(String timeStamp) throws ParseException {
        final SimpleDateFormat formatD = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        return formatD.parse(timeStamp);
    }

    /**
     * Shows the provided {@link RenderedImage} ina {@link JFrame} using the provided <code>title</code> as the frame's title.
     * 
     * @param image to show.
     * @param title to use.
     */
    static void show(RenderedImage image, String title) {
        ImageIOUtilities.visualize(image, title);

    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        TestRunner.run(NetCDFMosaicReaderTest.suite());

    }

    @Before
    public void init() {

        // make sure CRS ordering is correct
        System.setProperty("org.geotools.referencing.forceXY", "true");
        System.setProperty("user.timezone", "GMT");
        System.setProperty("org.geotools.shapefile.datetime", "true");
        CRS.reset("all");

        INTERACTIVE = TestData.isInteractiveTest();
    }

    @AfterClass
    public static void close() {
        System.clearProperty("org.geotools.referencing.forceXY");
        System.clearProperty("user.timezone");
        System.clearProperty("org.geotools.shapefile.datetime");
        CRS.reset("all");
    }

    /**
     * returns an {@link AbstractGridCoverage2DReader} for the provided {@link URL} and for the providede {@link AbstractGridFormat}.
     * 
     * @param testURL points to a valid object to create an {@link AbstractGridCoverage2DReader} for.
     * @param format to use for instantiating such a reader.
     * @return a suitable {@link ImageMosaicReader}.
     * @throws FactoryException
     * @throws NoSuchAuthorityCodeException
     */
    static ImageMosaicReader getReader(URL testURL, final AbstractGridFormat format)
            throws NoSuchAuthorityCodeException, FactoryException {

        // final Hints hints= new Hints(Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM, CRS.decode("EPSG:4326", true));
        return getReader(testURL, format, null);

    }

    static ImageMosaicReader getReader(URL testURL, final AbstractGridFormat format, Hints hints) {
        // Get a reader
        final ImageMosaicReader reader = (ImageMosaicReader) format.getReader(testURL, hints);
        Assert.assertNotNull(reader);
        return reader;
    }

}