/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2002-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-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;
import java.util.Random;
import java.awt.Point;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import javax.media.jai.RenderedOp;
import javax.media.jai.PlanarImage;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.test.DependsOn;
import org.geotoolkit.coverage.grid.ViewType;
import org.geotoolkit.coverage.grid.GridCoverage2D;
import org.geotoolkit.coverage.grid.GridCoverageFactory;
import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.junit.*;
import static org.junit.Assert.*;
/**
* Tests the {@link SampleTranscoder} implementation. Image adapter depends
* heavily on {@link CategoryList}, so this one should be tested first.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 3.11
*
* @since 2.1
*/
@DependsOn(SampleDimensionTest.class)
public final strictfp class SampleTranscoderTest extends org.geotoolkit.test.TestBase {
/**
* Small value for comparisons. Remind: transformed values are stored in a new image
* using the {@code float} data type. So we can't expected as much precision than with
* a {@code double} data type.
*/
private static final double EPS = 1E-4;
/**
* Random number generator for this test.
*/
private static final Random random = new Random(6215962897884256696L);
/**
* Creates a dummy sample dimensions for temperature and random qualitative categories.
*/
private GridSampleDimension createTemperatureBand() {
return new GridSampleDimension("Temperature", new Category[] {
new Category("No data", null, 0),
new Category("Land", null, 1),
new Category("Clouds", null, 2),
new Category("Temperature", null, 3, 100, 0.1, 5),
new Category("Foo", null, 100, 160, -1, 3),
new Category("Tarzan", null, 160)
}, null);
}
/**
* Tests the transformation using a random raster with only one band.
*
* @throws TransformException If an error occurred while transforming a value.
*/
@Test
public void testOneBand() throws TransformException {
final GridSampleDimension band1 = createTemperatureBand();
assertFalse(testOneBand(1, 0) instanceof RenderedOp);
assertTrue(testOneBand(.8, 2) instanceof RenderedOp);
assertTrue(testOneBand(band1) instanceof RenderedOp);
}
/**
* Tests the transformation using a random raster with only one band.
* A sample dimension with only one category will be created using the given scale and
* offset factors.
*
* @param scale The scale factor.
* @param offset The offset value.
* @return The transformed image.
*/
private static RenderedImage testOneBand(final double scale, final double offset) throws TransformException {
final Category category = new Category("Values", null, 0, 256, scale, offset);
return testOneBand(new GridSampleDimension("Measure", new Category[] {category}, null));
}
/**
* Tests the transformation using a random raster with only one band.
*
* @param band The sample dimension for the only band.
* @return The transformed image.
*/
private static RenderedImage testOneBand(final GridSampleDimension band) throws TransformException {
final int SIZE = 64;
/*
* Constructs a 64x64 image with random values.
* Samples values are integer in the range 0..160 inclusive.
*/
final BufferedImage source = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_BYTE_INDEXED);
final DataBufferByte buffer = (DataBufferByte) source.getRaster().getDataBuffer();
final byte[] array = buffer.getData(0);
for (int i=0; i<array.length; i++) {
array[i] = (byte) random.nextInt(161);
}
GridCoverage2D coverage = createGridCoverage2D(source, band);
/*
* Apply the operation. The SampleTranscoder class is suppose to transform our
* integers into real-world values. Check if the result use floating-points.
*/
final RenderedImage target = coverage.view(ViewType.GEOPHYSICS).getRenderedImage();
assertEquals(DataBuffer.TYPE_BYTE, source.getSampleModel().getDataType());
if (coverage.getRenderedImage() != target) {
assertEquals(DataBuffer.TYPE_FLOAT, target.getSampleModel().getDataType());
}
/*
* Now, gets the data as an array and compare it with the expected values.
*/
double[] sourceData = source.getData().getSamples(0, 0, SIZE, SIZE, 0, (double[]) null);
double[] targetData = target.getData().getSamples(0, 0, SIZE, SIZE, 0, (double[]) null);
band.getSampleToGeophysics().transform(sourceData, 0, sourceData, 0, sourceData.length);
CategoryListTest.compare(sourceData, targetData, EPS);
/*
* Construct a new image with the resulting data, and apply an inverse transformation.
* Compare the resulting values with the original data.
*/
RenderedImage back = PlanarImage.wrapRenderedImage(target).getAsBufferedImage();
coverage = createGridCoverage2D(back, band.geophysics(true));
back = coverage.view(ViewType.PACKED).getRenderedImage();
assertEquals(DataBuffer.TYPE_BYTE, back.getSampleModel().getDataType());
sourceData = source.getData().getSamples(0, 0, SIZE, SIZE, 0, (double[]) null);
targetData = back.getData().getSamples(0, 0, SIZE, SIZE, 0, (double[]) null);
CategoryListTest.compare(sourceData, targetData, 1.0 + EPS);
/*
* Returns the "geophysics view" of the image.
*/
return target;
}
/**
* Creates a {@code GridCoverage2D} instance for the given image using a random envelope.
* We don't care about the envelope for this test suite, so an identity transform is most
* convenient in order to work like in pixel coordinates.
*/
private static GridCoverage2D createGridCoverage2D(RenderedImage image, GridSampleDimension band) {
final MathTransform identity = MathTransforms.identity(2);
final GridCoverageFactory factory = CoverageFactoryFinder.getGridCoverageFactory(null);
return factory.create("Test", image, CommonCRS.WGS84.normalizedGeographic(),
identity, new GridSampleDimension[] {band}, null, null);
}
/**
* Creates a {@code GridCoverage2D} instance for the given image using a random envelope.
* We don't care about the envelope for this test suite, so an identity transform is most
* convenient in order to work like in pixel coordinates.
*/
private static GridCoverage2D createGridCoverage2D(RenderedImage image, Category category) {
return createGridCoverage2D(image, new GridSampleDimension(category.getName(),
new Category[] {category}, null));
}
/**
* Tests a raster of type {@code TYPE_USHORT}, both with signed and unsigned categories.
*/
@Test
public void testTypeUShort() {
/*
* Creates a dummy image. We don't care about the colors for this test.
* However we want the values in the upper-left corner to be negatives,
* and the values in the lower-right corner to be positives.
*/
final int[] values = new int[] {0, 1, 2, 10, 100, 255, 256, 1000, 10000};
final double scale = 0.1;
final double offset = 1.0;
final byte[] RGB = new byte[0x10000];
for (int i=0; i<RGB.length; i++) {
RGB[i] = (byte) i;
}
final IndexColorModel cm = new IndexColorModel(16, RGB.length, RGB, RGB, RGB);
final WritableRaster raster = cm.createCompatibleWritableRaster(2, values.length);
int sign = 1;
for (int i=0; i<=1; i++) {
for (int j=0; j<values.length; j++) {
raster.setSample(i, j, 0, values[j]*sign);
}
sign = -sign;
}
final BufferedImage image = new BufferedImage(cm, raster, false, null);
final Point point = new Point();
double[] buffer = null;
/*
* Tests unsigned categories first, then signed categories.
*
* NOTE: JAI bug? See the comment in the ViewsManager.geophysics(...) method (look in the
* code block setting the parameters for the JAI "Rescale" operation). To test this issue
* in a debugger, put a break point on the 'createGridCoverage2D(...)' line, then jump to
* the above-cited ViewsManager.geophysics(...) method. The first loop iteration in this
* test is the interesting one.
*/
boolean forceSigned = false;
do {
final int lower, upper;
if (forceSigned) {
lower = Short.MIN_VALUE;
upper = Short.MAX_VALUE;
} else {
lower = 0;
upper = 0x10000;
}
Category category = new Category("Test", null, lower, upper, scale, offset);
GridCoverage2D coverage = createGridCoverage2D(image, category).view(ViewType.GEOPHYSICS);
for (int i=0; i<=1; i++) {
for (int j=0; j<values.length; j++) {
point.x = i;
point.y = j;
int expected = values[j];
if (i != 0) {
// Testing negative values.
expected = -expected;
if (!forceSigned) {
expected &= 0xFFFF;
}
}
buffer = coverage.evaluate(point, buffer);
String message = "Testing the " + expected + " sample value (stored as " +
raster.getSample(i, j, 0) + " in the raster)";
if (false) { // set to 'true' for tracing the operations.
System.out.println(message);
}
assertEquals(message, expected*scale + offset, buffer[0], EPS);
}
}
} while ((forceSigned = !forceSigned) == true);
}
}