/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2007-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2007-2012, Geomatys
*
* 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.geotoolkit.coverage.sql;
import java.io.File;
import java.awt.Rectangle;
import java.awt.image.RenderedImage;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import java.util.List;
import java.util.Set;
import org.opengis.geometry.Envelope;
import org.apache.sis.test.DependsOn;
import org.geotoolkit.test.TestData;
import org.geotoolkit.internal.sql.table.CatalogTestBase;
import org.geotoolkit.image.io.plugin.WorldFileImageReader;
import org.geotoolkit.image.io.plugin.TextMatrixImageReader;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.coverage.grid.ViewType;
import org.apache.sis.geometry.AbstractEnvelope;
import org.apache.sis.measure.MeasurementRange;
import org.apache.sis.measure.Range;
import org.junit.*;
import static org.junit.Assert.*;
/**
* Tests {@link WritableGridCoverageTable}.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.16
*
* @since 3.12
*/
@DependsOn(GridCoverageTableTest.class)
public final strictfp class WritableGridCoverageTableTest extends CatalogTestBase {
/**
* The file which contains geostrophic current data.
*/
public static final String GEOSTROPHIC_CURRENT_FILE = "Iroise/champs.r3_23-05-2007.nc";
/**
* The NcML file which is an aggregation of 6 NetCDF files (2 variables at 3 different dates).
*/
public static final String AGGREGATION_FILE = "World/Coriolis/Aggregation.ncml";
/**
* A temporary layer in which images will be inserted.
*/
private static final String TEMPORARY_LAYER = "Temporary";
/**
* Tolerance factor for floating point comparisons.
*/
private static final double EPS = 1E-7;
/**
* {@code true} if {@link #createTemporaryLayer()} has created a temporary layer.
*/
private boolean layerCreated;
/**
* Registers <cite>World File Readers</cite>.
* They are required by {@link #textMatrix()}.
*/
@BeforeClass
public static void registerWorldFiles() {
WorldFileImageReader.Spi.registerDefaults(null);
}
/**
* Unregisters the <cite>World File Readers</cite>.
*/
@AfterClass
public static void unregisterWorldFiles() {
WorldFileImageReader.Spi.unregisterDefaults(null);
}
/**
* Creates a new test suite.
*/
public WritableGridCoverageTableTest() {
super(WritableGridCoverageTable.class);
}
/**
* Creates a new temporary layer in which to insert the new images.
*
* @throws SQLException If an error occurred while creating the temporary layer.
*/
@Before
public void createTemporaryLayer() throws SQLException {
if (canTest()) {
final LayerTable table = getDatabase().getTable(LayerTable.class);
assertTrue(table.createIfAbsent(TEMPORARY_LAYER));
table.release();
layerCreated = true;
}
}
/**
* Deletes the temporary layer and all coverages included in it.
*
* @throws SQLException If an error occurred while deleting the temporary layer.
*/
@After
public void deleteTemporaryLayer() throws SQLException {
if (layerCreated) {
final LayerTable table = getDatabase().getTable(LayerTable.class);
assertEquals(1, table.delete(TEMPORARY_LAYER));
table.release();
layerCreated = false;
}
}
/**
* Returns {@code true} if the given range is bounded, or {@code false} if unbounded.
*/
private static boolean isBounded(final Range<?> range) {
final Comparable<?> min = range.getMinValue();
final Comparable<?> max = range.getMaxValue();
assertEquals(min != null, max != null);
return (min != null) & (max != null);
}
/**
* Tests insertion for ASCII data. This test reuses the {@code "matrix.txt"} test file
* from the {@code geotk-coverageio} module. The {@code ".txt"} file is completed by
* {@code "matrix.tfw"} and {@code "matrix.prj"} files.
*
* @throws Exception If a SQL, I/O or referencing error occurred.
*
* @since 3.14
*/
@Test
@Ignore("Need to check test-data location")
public void testMatrix() throws Exception {
final WritableGridCoverageTable table = getDatabase().getTable(WritableGridCoverageTable.class);
final CoverageDatabase database = new CoverageDatabase((TableFactory) getDatabase());
final CoverageDatabaseController controller = new CoverageDatabaseController() {
/**
* Checks the parameter values before their insertion in the database.
*/
@Override
public void coverageAdding(CoverageDatabaseEvent event, NewGridCoverageReference reference) {
assertEquals("test-data", reference.path.getFileName().toString());
assertEquals("matrix", reference.filename);
assertEquals("txt", reference.extension);
assertEquals("matrix", reference.format);
assertEquals("imageIndex", 0, reference.imageIndex);
assertEquals(new Rectangle(20, 42), reference.imageBounds);
assertEquals( 0, reference.gridToCRS.getShearX(), 0);
assertEquals( 0, reference.gridToCRS.getShearY(), 0);
assertEquals( 1000, reference.gridToCRS.getScaleX(), 0);
assertEquals( -1000, reference.gridToCRS.getScaleY(), 0);
assertEquals(-10000, reference.gridToCRS.getTranslateX(), 0);
assertEquals( 21000, reference.gridToCRS.getTranslateY(), 0);
assertEquals( 6001, reference.horizontalSRID);
assertEquals( 0, reference.verticalSRID);
assertNull ( reference.verticalValues);
assertNull ( reference.dateRanges);
}
@Override
public Collection<String> filterImages(List<String> images, boolean multiSelectionAllowed) throws DatabaseVetoException {
fail("Should not need to be invoked when there is exactly one image to insert.");
return images;
}
};
final Set<File> files = Collections.singleton(TestData.file(TextMatrixImageReader.class, "matrix.txt"));
table.setLayer(TEMPORARY_LAYER);
/*
* TODO: Ugly patch below: set the locale to an anglo-saxon one, so we can parse the
* matrix numbers. We should find an other way to provide the locale to the reader...
*/
final Locale locale = Locale.getDefault();
try {
Locale.setDefault(Locale.CANADA);
assertEquals(1, table.addEntries(files, database, controller));
} finally {
Locale.setDefault(locale);
}
/*
* At this point, the entry has been added to the database. Now read
* the GridCoverages table in order to get the entry we just added.
*/
final GridCoverageEntry entry = table.getEntry();
assertEquals("matrix.txt", entry.getFile(File.class).getName());
assertEquals("matrix", entry.getImageFormat());
assertFalse ("Expected unbounded range.", isBounded(entry.getZRange()));
assertFalse ("Expected unbounded range.", isBounded(entry.getTimeRange()));
final Envelope envelope = entry.getEnvelope();
assertEquals(-10000, envelope.getMinimum(0), 0);
assertEquals( 10000, envelope.getMaximum(0), 0);
assertEquals(-21000, envelope.getMinimum(1), 0);
assertEquals( 21000, envelope.getMaximum(1), 0);
table.release();
/*
* Clean-up: delete the "matrix" format, so that the next execution will
* recreate a new format. Note that we need to delete the layer first.
* We also perform an opportunist check of the format inserted by the above code.
*/
deleteTemporaryLayer();
final FormatTable ft = getDatabase().getTable(FormatTable.class);
final FormatEntry format = ft.getEntry("matrix");
assertEquals(1, ft.delete("matrix"));
ft.release();
/*
* Verify the format.
*/
assertEquals("matrix", format.identifier);
assertEquals("matrix", format.imageFormat);
assertEquals("grayscale", format.paletteName);
assertEquals(ViewType.GEOPHYSICS, format.viewType);
final MeasurementRange<?>[] range = format.getSampleValueRanges();
assertNotNull(range);
assertEquals(1, range.length);
// The minimum value is actually -1.893, but we didn't specified the pad value.
assertEquals(-9999, range[0].getMinDouble(true), 1E-4);
assertEquals(31.140, range[0].getMaxDouble(true), 1E-4);
assertNull(range[0].unit());
}
/**
* Tests insertion for NetCDF images with two bands but no elevation.
* The variables declared in the files are "h0", "xe", "u" and "v".
* This tests will insert the second variable, namely "xe".
* The same file contains values at many different dates.
* <p>
* This test creates a temporary layer and fills it with the same data than
* the ones declared in the {@value LayerTableTest#GEOSTROPHIC_CURRENT} layer.
*
* @throws Exception If a SQL, I/O or referencing error occurred.
*/
@Test
public void testGeostrophicCurrent2D() throws Exception {
final WritableGridCoverageTable table = getDatabase().getTable(WritableGridCoverageTable.class);
final CoverageDatabase database = new CoverageDatabase((TableFactory) getDatabase());
final CoverageDatabaseController controller = new CoverageDatabaseController() {
/**
* Checks the parameter values before their insertion in the database.
*
* Forces also the format to one of the pre-defined ones, since we don't
* want to test the creation of new entries in the format table.
*/
@Override
public void coverageAdding(CoverageDatabaseEvent event, NewGridCoverageReference reference) {
assertEquals ("Iroise", reference.path.getFileName().toString());
assertEquals ("champs.r3_23-05-2007", reference.filename);
assertEquals ("nc", reference.extension);
assertEquals ("NetCDF", reference.format);
assertEquals ("imageIndex", 0, reference.imageIndex);
assertEquals (new Rectangle(273, 423), reference.imageBounds);
assertEquals ( 0.0, reference.gridToCRS.getShearX(), 0.0);
assertEquals ( 0.0, reference.gridToCRS.getShearY(), 0.0);
assertEquals ( 0.0040461, reference.gridToCRS.getScaleX(), EPS);
assertEquals ( 0.0026991, reference.gridToCRS.getScaleY(), EPS);
assertEquals (-5.3390946, reference.gridToCRS.getTranslateX(), EPS);
assertEquals (47.6543427, reference.gridToCRS.getTranslateY(), EPS);
assertEquals ("Horizontal", 4326, reference.horizontalSRID);
assertEquals ("Expected no z.", 0, reference.verticalSRID);
assertNull ("Expected no z.", reference.verticalValues);
assertNotNull("Expected dates", reference.dateRanges);
reference.format = "Mars (u,v)";
reference.horizontalSRID = 4326; // TODO: need to auto-detect.
}
@Override
public Collection<String> filterImages(List<String> images, boolean multiSelectionAllowed) throws DatabaseVetoException {
assertTrue("Multi-selection should be allowed for NetCDF files.", multiSelectionAllowed);
assertArrayEquals("Expected one image for each variable.", new String[] {
"h0", "xe", "u", "v"
}, images.toArray());
// Select the "xe" variable for this test.
return Collections.singleton(images.get(1));
}
};
requireImageData();
final Set<File> files = Collections.singleton(toImageFile(GEOSTROPHIC_CURRENT_FILE));
table.setLayer(TEMPORARY_LAYER);
assertEquals("Expected the addition of many entries, one for each date.",
285, table.addEntries(files, database, controller));
/*
* At this point, the entries have been added to the database. Now
* test each individual entry, including a test of coverage reading.
*/
int imageIndex = 285;
double lastTime = Double.POSITIVE_INFINITY;
for (final GridCoverageEntry entry : table.getEntries()) {
assertEquals("Every entry are expected to use the same input file.",
"champs.r3_23-05-2007.nc", entry.getFile(File.class).getName());
assertEquals("Image indices are expected to be decreasing when iterating " +
"from the most recent image to the oldest one.",
imageIndex--, entry.getIdentifier().imageIndex);
assertEquals("NetCDF", entry.getImageFormat());
assertTrue ("Expected bounded range.", isBounded(entry.getZRange()));
assertTrue ("Expected bounded range.", isBounded(entry.getTimeRange()));
/*
* Check the envelope, we should be the same for all entry in
* all dimensions except the time dimension.
*/
final Envelope envelope = entry.getEnvelope();
assertEquals("Expected a three-dimensional coverage.", 3, envelope.getDimension());
assertEquals(-5.3390946, envelope.getMinimum(0), EPS);
assertEquals(-4.2345093, envelope.getMaximum(0), EPS);
assertEquals(47.6543427, envelope.getMinimum(1), EPS);
assertEquals(48.7960646, envelope.getMaximum(1), EPS);
final double time = envelope.getMedian(2);
assertTrue("Expected ordering from most recent to oldest entries.", time < lastTime);
lastTime = time;
/*
* Inspect the coverage. For performance raison in this test suite,
* we test only some images (they should all be similar anyway).
*/
if ((imageIndex % 10) == 0) {
final GridCoverage2D coverage = entry.getCoverage(null);
assertSame("The coverage should be the geophysics view.",
coverage, coverage.view(ViewType.GEOPHYSICS));
assertNotSame("The coverage should not be packed.",
coverage, coverage.view(ViewType.PACKED));
assertTrue(((AbstractEnvelope) envelope).equals(coverage.getEnvelope(), EPS, false));
/*
* Inspect the image of the rendered view. We expect two bands,
* despite the color model being an instance of IndexColorModel.
*/
final RenderedImage image = coverage.view(ViewType.RENDERED).getRenderedImage();
assertEquals("Expected two bands.", 2, image.getSampleModel().getNumBands());
// org.geotoolkit.gui.swing.image.OperationTreeBrowser.show(image);
}
}
assertEquals("Missing entries.", 0, imageIndex);
table.release();
}
/**
* Tests insertion of a NcML file which is an aggregation of 3 NetCDF files.
*
* @throws Exception If a SQL, I/O or referencing error occurred.
*/
@Test
@Ignore("CDL has changed while upgrading NetCDF dependency to 4.3.21")
public void testNcML() throws Exception {
final WritableGridCoverageTable table = getDatabase().getTable(WritableGridCoverageTable.class);
final CoverageDatabase database = new CoverageDatabase((TableFactory) getDatabase());
final CoverageDatabaseController controller = new CoverageDatabaseController() {
/**
* Checks the parameter values before their insertion in the database.
*/
@Override
public void coverageAdding(CoverageDatabaseEvent event, NewGridCoverageReference reference) {
assertEquals ("Coriolis", reference.path.getFileName().toString());
assertEquals ("Aggregation", reference.filename);
assertEquals ("ncml", reference.extension);
assertEquals ("Coriolis (salinity)", reference.format);
assertEquals ("imageIndex", 0, reference.imageIndex);
assertEquals (new Rectangle(720, 499), reference.imageBounds);
assertEquals ( 0.00, reference.gridToCRS.getShearX(), 0);
assertEquals ( 0.00, reference.gridToCRS.getShearY(), 0);
assertEquals ( 55597.46, reference.gridToCRS.getScaleX(), 0.01);
assertEquals ( 55597.46, reference.gridToCRS.getScaleY(), 0.01);
assertEquals (-19959489.33, reference.gridToCRS.getTranslateX(), 0.01);
assertEquals (-13843768.17, reference.gridToCRS.getTranslateY(), 0.01);
assertEquals ("TODO", 0, reference.horizontalSRID);
assertEquals ("TODO", 0, reference.verticalSRID);
assertNotNull("Expected depths", reference.verticalValues);
assertNotNull("Expected dates", reference.dateRanges);
reference.horizontalSRID = 3395; // TODO: need to auto-detect.
reference.verticalSRID = 5715; // TODO: need to auto-detect.
}
@Override
public Collection<String> filterImages(List<String> images, boolean multiSelectionAllowed) throws DatabaseVetoException {
assertTrue("Multi-selection should be allowed for NetCDF files.", multiSelectionAllowed);
assertArrayEquals("Expected one image for each variable.", new String[] {
"temperature", "temperature_pct_variance", "salinity", "salinity_pct_variance"
}, images.toArray());
// Select the "salinity" variable for this test.
return Collections.singleton(images.get(2));
}
};
requireImageData();
final Set<File> files = Collections.singleton(toImageFile(AGGREGATION_FILE));
table.setLayer(TEMPORARY_LAYER);
assertEquals("Expected the addition of many entries, one for each date.",
3, table.addEntries(files, database, controller));
/*
* At this point, the entries have been added to the database. Now
* test each individual entry, including a test of coverage reading.
*/
final String[] expectedFilenames = {
"OA_RTQCGL01_20070620_FLD_PSAL.nc",
"OA_RTQCGL01_20070613_FLD_PSAL.nc",
"OA_RTQCGL01_20070606_FLD_PSAL.nc"
};
int count = 0;
double lastTime = Double.POSITIVE_INFINITY;
for (final GridCoverageEntry entry : table.getEntries()) {
assertEquals(expectedFilenames[count++], entry.getFile(File.class).getName());
assertEquals(1, entry.getIdentifier().imageIndex);
assertEquals("NetCDF", entry.getImageFormat());
assertTrue ("Expected bounded range.", isBounded(entry.getZRange()));
assertTrue ("Expected bounded range.", isBounded(entry.getTimeRange()));
/*
* Check the envelope, we should be the same for all entry in
* all dimensions except the time dimension.
*/
final Envelope envelope = entry.getEnvelope();
assertEquals("Expected a four-dimensional coverage.", 4, envelope.getDimension());
assertEquals(-19959489.33, envelope.getMinimum(0), 0.01);
assertEquals( 20070684.26, envelope.getMaximum(0), 0.01);
assertEquals(-13843768.17, envelope.getMinimum(1), 0.01);
assertEquals( 13899365.64, envelope.getMaximum(1), 0.01);
assertEquals( 5, envelope.getMinimum(2), 0);
assertEquals( 1950, envelope.getMaximum(2), 0);
final double time = envelope.getMedian(3);
assertTrue("Expected ordering from most recent to oldest entries.", time < lastTime);
lastTime = time;
/*
* Inspect the coverage. For performance raison, we inspect
* only the last image (all other images should be similar).
*/
if (count == expectedFilenames.length) {
final GridCoverage2D coverage = entry.getCoverage(null);
assertSame("The coverage should be the geophysics view.",
coverage, coverage.view(ViewType.GEOPHYSICS));
assertNotSame("The coverage should not be packed.",
coverage, coverage.view(ViewType.PACKED));
assertTrue(((AbstractEnvelope) envelope).equals(coverage.getEnvelope(), 0.01, false));
/*
* Inspect the image of the rendered view. We expect two bands,
* despite the color model being an instance of IndexColorModel.
*/
final RenderedImage image = coverage.view(ViewType.RENDERED).getRenderedImage();
assertEquals("Expected one band.", 1, image.getSampleModel().getNumBands());
// org.geotoolkit.gui.swing.image.OperationTreeBrowser.show(image);
}
}
assertEquals("Missing entries.", expectedFilenames.length, count);
table.release();
}
}