/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2010-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2010-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.io;
import java.io.File;
import java.io.LineNumberReader;
import java.io.ByteArrayOutputStream;
import java.util.Locale;
import java.text.ParseException;
import java.io.IOException;
import java.io.StringWriter;
import javax.imageio.ImageWriter;
import org.apache.sis.test.DependsOn;
import org.apache.sis.internal.system.OS;
import org.geotoolkit.test.TestData;
import org.geotoolkit.io.LineFormat;
import org.geotoolkit.io.LineReader;
import org.geotoolkit.io.LineReaders;
import org.geotoolkit.image.SampleModels;
import org.apache.sis.geometry.Envelope2D;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.image.io.plugin.TextMatrixImageReaderTest;
import org.geotoolkit.test.PlatformDependentTest;
import org.geotoolkit.test.image.ImageTestBase;
import org.junit.*;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
/**
* Tests {@link ImageCoverageWriter}. This test will read the {@code "matrix.txt"} file
* defined in the {@code org/geotoolkit/image/io/plugin/test-data} directory because it
* is the easiest one to debug.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.17
*
* @since 3.14
*/
@DependsOn(ImageCoverageReaderTest.class)
public final strictfp class ImageCoverageWriterTest extends ImageTestBase {
/**
* Tolerance factor when comparing values from the {@code "matrix.txt"} file.
* We set the tolerance to the first decimal digit after the significant digits.
*/
private static final double EPS = 1E-4;
/**
* Creates a new test suite.
*/
public ImageCoverageWriterTest() {
super(ImageCoverageWriter.class);
}
/**
* Registers a "matrix" reader forced to the US format.
*/
@BeforeClass
public static void registerReaderUS() {
ImageCoverageReaderTest.registerReaderUS();
}
/**
* Unregisters the reader defined by {@link #registerReaderUS()}.
*/
@AfterClass
public static void deregisterReaderUS() {
ImageCoverageReaderTest.deregisterReaderUS();
}
/**
* Creates a {@link GridCoverage2D} from the given file.
*/
private static GridCoverage2D read(final String file) throws IOException, CoverageStoreException {
final ImageCoverageReader reader = new ImageCoverageReader();
reader.setInput(TestData.file(TextMatrixImageReaderTest.class, file));
final GridCoverage2D coverage = reader.read(0, null);
reader.dispose();
return coverage;
}
/**
* Asserts that the numbers in the given string are equal. Numbers are parsed using the
* given locale, which may not be the same for the expected and the actual strings.
*/
private static void assertMatrixEquals(
final LineReader expected, final Locale expectedLocale,
final LineReader actual, final Locale actualLocale,
final double missingValue) throws IOException, ParseException
{
final LineFormat f1 = new LineFormat(expectedLocale);
final LineFormat f2 = new LineFormat(actualLocale);
double[] expectedArray = null;
double[] actualArray = null;
String line;
while ((line = expected.readLine()) != null) {
line = line.trim();
if (line.isEmpty() || line.startsWith("#")) {
continue;
}
final String actualLine = actual.readLine();
assertNotNull("Missing line.", actualLine);
f1.setLine(line);
f2.setLine(actualLine);
expectedArray = f1.getValues(expectedArray);
actualArray = f2.getValues(actualArray);
assertEquals("Line length differ.", expectedArray.length, actualArray.length);
for (int i=0; i<expectedArray.length; i++) {
double expectedValue = expectedArray[i];
if (expectedValue == missingValue) {
expectedValue = Double.NaN;
}
double actualValue = actualArray[i];
if (actualValue == missingValue) {
actualValue = Double.NaN;
}
assertEquals("Sample value differ.", expectedValue, actualValue, EPS);
}
}
while ((line = actual.readLine()) != null) {
assertEquals("Extra line.", "", line.trim());
}
}
/**
* Convenience method invoking the above {@code assertImageEquals} for an expected
* string in a fixed locale, and an actual string given explicitly in current locale.
*/
private static void assertMatrixEquals(final String expected, final String actual,
final double missingValue) throws IOException, ParseException
{
assertMatrixEquals(LineReaders.wrap(expected), Locale.CANADA,
LineReaders.wrap(actual), Locale.getDefault(Locale.Category.FORMAT),
missingValue);
}
/**
* Convenience method invoking the above {@code assertImageEquals} for an expected string read
* from the given file in fixed locale, and an actual string given explicitly in current locale.
*/
private static void assertMatrixEqualsFile(final String expectedFile, final String actual,
final double missingValue) throws IOException, ParseException
{
try (LineNumberReader in = TestData.openReader(TextMatrixImageReaderTest.class, expectedFile)) {
assertMatrixEquals(LineReaders.wrap(in), Locale.CANADA,
LineReaders.wrap(actual), Locale.getDefault(Locale.Category.FORMAT),
missingValue);
}
}
/**
* Writes the full image.
*
* @throws IOException If the text file can not be open (should not happen).
* @throws CoverageStoreException Should not happen.
* @throws ParseException Should not happen.
*/
@Test
public void writeFull() throws IOException, CoverageStoreException, ParseException {
final GridCoverage2D coverage = read("matrix.txt");
final ImageCoverageWriterInspector writer = new ImageCoverageWriterInspector("writeFull", "matrix");
final StringWriter buffer = new StringWriter();
writer.setOutput(buffer);
writer.write(coverage, null);
if (out != null) {
out.println(writer);
}
writer.assertNoDifference();
writer.assertRectifiedGridEquals(1000, -1000, -10000, 21000);
writer.dispose();
buffer.close(); // As a matter of principle.
assertMatrixEqualsFile("matrix.txt", buffer.toString(), -9999);
}
/**
* Writes a region of the image.
*
* @throws IOException If the text file can not be open (should not happen).
* @throws CoverageStoreException Should not happen.
* @throws ParseException Should not happen.
*/
@Test
public void writeRegion() throws IOException, CoverageStoreException, ParseException {
final GridCoverage2D coverage = read("matrix.txt");
final ImageCoverageWriterInspector writer = new ImageCoverageWriterInspector("writeRegion");
final GridCoverageWriteParam param = new GridCoverageWriteParam();
param.setFormatName("matrix");
param.setEnvelope(new Envelope2D(null, -1000, -2000, 8000 - -1000, 12000 - -2000));
final StringWriter buffer = new StringWriter();
writer.setOutput(buffer);
writer.write(coverage, param);
if (out != null) {
out.println(writer);
}
writer.assertNoDifference();
writer.assertRectifiedGridEquals(1000, -1000, -1000, 12000);
writer.dispose();
buffer.close(); // As a matter of principle.
assertMatrixEquals(
"12.783 12.499 -9999 -9999 -9999 -9999 -9999 -9999 -9999\n" +
"14.020 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999\n" +
"15.162 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999\n" +
"15.994 -9999 -9999 19.006 -9999 -9999 -9999 -9999 -9999\n" +
"17.356 20.674 -9999 -9999 -9999 -9999 -9999 -9999 -9999\n" +
"19.070 -9999 21.312 -9999 -9999 -9999 -9999 -9999 16.912\n" +
"19.766 -9999 -9999 -9999 -9999 -9999 -9999 -9999 22.195\n" +
" -9999 -9999 -9999 -9999 31.140 -9999 -9999 -9999 27.057\n" +
" -9999 -9999 -9999 28.996 -9999 -9999 29.741 27.922 29.127\n" +
" -9999 -9999 -9999 -9999 28.365 -9999 29.445 28.823 29.871\n" +
" -9999 -9999 -9999 -9999 27.067 28.951 29.540 29.253 29.824\n" +
"28.094 28.104 -9999 -9999 28.954 29.224 29.287 29.609 29.188\n" +
"25.454 25.820 -9999 -9999 28.810 29.347 29.633 29.522 28.902\n" +
"26.769 25.656 -9999 26.820 27.631 28.311 29.058 29.028 27.949\n",
buffer.toString(), -9999);
}
/**
* Writes the same region than above, with subsampling.
*
* @throws IOException If the text file can not be open (should not happen).
* @throws CoverageStoreException Should not happen.
* @throws ParseException Should not happen.
*/
@Test
public void writeSubsampledRegion() throws IOException, CoverageStoreException, ParseException {
final GridCoverage2D coverage = read("matrix.txt");
final ImageCoverageWriterInspector writer = new ImageCoverageWriterInspector("writeSubsampledRegion");
final GridCoverageWriteParam param = new GridCoverageWriteParam();
param.setFormatName("matrix");
param.setEnvelope(new Envelope2D(null, -1000, -2000, 8000 - -1000, 12000 - -2000));
param.setResolution(2000, 3000);
final StringWriter buffer = new StringWriter();
writer.setOutput(buffer);
writer.write(coverage, param);
if (out != null) {
out.println(writer);
}
writer.assertNoDifference();
writer.assertRectifiedGridEquals(2000, -3000, -1000, 12000);
writer.dispose();
buffer.close(); // As a matter of principle.
assertMatrixEquals(
"12.783 -9999 -9999 -9999 -9999\n" +
"15.994 -9999 -9999 -9999 -9999\n" +
"19.766 -9999 -9999 -9999 22.195\n" +
" -9999 -9999 28.365 29.445 29.871\n" +
"25.454 -9999 28.810 29.633 28.902\n",
buffer.toString(), -9999);
}
/**
* Writes a small region, with a scale factor. It should force the {@link GridCoverageWriter}
* to perform a {@code WarpAffine} operation.
*
* @throws IOException If the text file can not be open (should not happen).
* @throws CoverageStoreException Should not happen.
* @throws ParseException Should not happen.
*/
@Test
public void writeScaledRegion() throws IOException, CoverageStoreException, ParseException {
final GridCoverage2D coverage = read("matrix.txt");
final ImageCoverageWriterInspector writer = new ImageCoverageWriterInspector("writeScaledRegion");
final GridCoverageWriteParam param = new GridCoverageWriteParam();
param.setFormatName("matrix");
param.setEnvelope(new Envelope2D(null, 4000, -2000, 8000 - 4000, 4000 - -2000));
param.setResolution(1000.0 / 2, 1000.0 / 3);
final StringWriter buffer = new StringWriter();
writer.setOutput(buffer);
writer.write(coverage, param);
if (out != null) {
out.println(writer);
}
writer.assertDifferenceEqualsScale(1.0 / 2, 1.0 / 3);
writer.assertRectifiedGridEquals(500, -1000.0 / 3, 4000, 4000);
writer.dispose();
buffer.close(); // As a matter of principle.
/*
* The expected matrix below is the 4×6 region in the lower right corner of the
* expected matrix declared in writeRegion() where each column is repeated once
* and echo row is repeated twice. The interpolation is nearest-neighbor.
*/
assertMatrixEquals(
" -9999 -9999 29.741 29.741 27.922 27.922 29.127 29.127\n" +
" -9999 -9999 29.741 29.741 27.922 27.922 29.127 29.127\n" +
" -9999 -9999 29.741 29.741 27.922 27.922 29.127 29.127\n" +
" -9999 -9999 29.445 29.445 28.823 28.823 29.871 29.871\n" +
" -9999 -9999 29.445 29.445 28.823 28.823 29.871 29.871\n" +
" -9999 -9999 29.445 29.445 28.823 28.823 29.871 29.871\n" +
"28.951 28.951 29.540 29.540 29.253 29.253 29.824 29.824\n" +
"28.951 28.951 29.540 29.540 29.253 29.253 29.824 29.824\n" +
"28.951 28.951 29.540 29.540 29.253 29.253 29.824 29.824\n" +
"29.224 29.224 29.287 29.287 29.609 29.609 29.188 29.188\n" +
"29.224 29.224 29.287 29.287 29.609 29.609 29.188 29.188\n" +
"29.224 29.224 29.287 29.287 29.609 29.609 29.188 29.188\n" +
"29.347 29.347 29.633 29.633 29.522 29.522 28.902 28.902\n" +
"29.347 29.347 29.633 29.633 29.522 29.522 28.902 28.902\n" +
"29.347 29.347 29.633 29.633 29.522 29.522 28.902 28.902\n" +
"28.311 28.311 29.058 29.058 29.028 29.028 27.949 27.949\n" +
"28.311 28.311 29.058 29.058 29.028 29.028 27.949 27.949\n" +
"28.311 28.311 29.058 29.058 29.028 29.028 27.949 27.949\n",
buffer.toString(), -9999);
}
/**
* Writes a small region, with a portion outside the valid area of source data.
*
* @throws IOException If the text file can not be open (should not happen).
* @throws CoverageStoreException Should not happen.
* @throws ParseException Should not happen.
*
* @todo Platforms other than MacOS produce random numbers in the area where pad values
* are expected. This is related to the JAI issue documented in the Resample2D
* class (nearest neighbor interpolation of floating point values).
*/
@Test
@PlatformDependentTest
public void writeExpandedUpperLeftRegion() throws IOException, CoverageStoreException, ParseException {
assumeTrue(OS.current() == OS.MAC_OS);
final GridCoverage2D coverage = read("matrix.txt");
final ImageCoverageWriterInspector writer = new ImageCoverageWriterInspector("writeOutsideRegion");
final GridCoverageWriteParam param = new GridCoverageWriteParam();
param.setFormatName("matrix");
param.setEnvelope(new Envelope2D(null, -12000, 24000 - 9000, 7000, 9000));
final StringWriter buffer = new StringWriter();
writer.setOutput(buffer);
writer.write(coverage, param);
if (out != null) {
out.println(writer);
}
writer.assertDifferenceEqualsTranslation(-2, -3);
writer.assertRectifiedGridEquals(1000, -1000, -12000, 24000);
writer.dispose();
buffer.close(); // As a matter of principle.
/*
* The expected matrix below is the upper-left corner of the full matrix, augmented
* by three rows and two columns of pad values.
*/
assertMatrixEquals(
" -9999 -9999 -9999 -9999 -9999 -9999 -9999\n" +
" -9999 -9999 -9999 -9999 -9999 -9999 -9999\n" +
" -9999 -9999 -9999 -9999 -9999 -9999 -9999\n" +
" -9999 -9999 -1.123 -1.348 -1.477 -1.399 -1.141\n" +
" -9999 -9999 -0.513 -0.842 -1.062 -9999 -1.113\n" +
" -9999 -9999 0.349 -0.352 -0.173 -0.754 -0.965\n" +
" -9999 -9999 1.061 -9999 0.273 -0.743 -0.819\n" +
" -9999 -9999 1.316 -9999 -9999 -9999 -9999\n" +
" -9999 -9999 1.766 -9999 -9999 -9999 -9999\n",
buffer.toString(), -9999);
}
/**
* Writes an image twice, asking for different parts. The purpose of this test is to ensure
* that {@link ImageWriter} are properly used (with the right output set) when recycled. For
* this test, we need an image writer which doesn't accept {@link File} object directly.
*
* @throws IOException If the text file can not be open (should not happen).
* @throws CoverageStoreException Should not happen.
*/
@Test
public void writeTwice() throws IOException, CoverageStoreException {
final ImageCoverageReader reader = new ImageCoverageReader();
reader.setInput(TestData.file(SampleModels.class, "Contour.png"));
final GridCoverage2D coverage = reader.read(0, null);
reader.dispose();
try (ByteArrayOutputStream buffer = new ByteArrayOutputStream(8192)) {
assertEquals("Expected an initially empty stream.", 0, buffer.size());
final GridCoverageWriteParam param = new GridCoverageWriteParam();
param.setFormatName("PNG");
final ImageCoverageWriterInspector writer = new ImageCoverageWriterInspector("writeTwice");
writer.setOutput(buffer);
writer.write(coverage, param);
if (out != null) {
out.println(writer);
}
writer.assertNoDifference();
final long length = buffer.size();
assertTrue("Empty file.", length > 0);
buffer.reset();
assertEquals("Expected an initially empty stream.", 0, buffer.size());
param.setEnvelope(new Envelope2D(null, 100, 50, 800, 1200));
param.setResolution(20, 30);
writer.setOutput(buffer);
writer.write(coverage, param);
if (out != null) {
out.println(writer);
}
assertTrue("Expected a smaller file", buffer.size() < length);
writer.dispose();
}
}
}