/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-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;
import java.util.Arrays;
import java.util.Random;
import org.opengis.referencing.operation.TransformException;
import org.geotools.resources.XArray;
import org.geotools.util.Range;
import org.junit.*;
import static org.junit.Assert.*;
/**
* Tests the {@link CategoryList} implementation.
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
*/
public final class CategoryListTest {
/**
* Set to {@code true} in order to print diagnostic messages.
*/
private static final boolean VERBOSE = false;
/**
* Small value for comparaisons.
*/
private static final double EPS = 1E-9;
/**
* Random number generator for this test.
*/
private static final Random random = new Random(1471753385855374101L);
/**
* Returns the specified value as an hexadecimal string. Usefull
* for comparing NaN values.
*/
private static String toHexString(final double value) {
return Integer.toHexString(Float.floatToRawIntBits((float)value));
}
/**
* Tests the {@link CategoryList#binarySearch} method.
*/
@Test
public void testBinarySearch() {
for (int pass=0; pass<50; pass++) {
final double[] array = new double[64];
for (int i=0; i<array.length; i++) {
array[i] = (random.nextInt(100) - 50) / 10;
}
Arrays.sort(array);
for (int i=0; i<300; i++) {
final double searchFor = (random.nextInt(150) - 75) / 10;
assertEquals("binarySearch", Arrays.binarySearch(array, searchFor),
CategoryList.binarySearch(array, searchFor));
}
/*
* Previous test didn't tested NaN values (which is the main difference
* between binarySearch method in Arrays and CategoryList). Now test it.
*/
final Category[] categories = new Category[array.length];
for (int i=0; i<categories.length; i++) {
categories[i] = new Category(String.valueOf(i), null, random.nextInt(100)).inverse;
}
Arrays.sort(categories, new CategoryList(new Category[0], null));
assertTrue("isSorted", CategoryList.isSorted(categories));
for (int i=0; i<categories.length; i++) {
array[i] = categories[i].inverse.minimum;
}
for (int i=0; i<categories.length; i++) {
final double expected = categories[i].inverse.minimum;
final int foundAt = CategoryList.binarySearch(array, expected);
final double actual = categories[foundAt].inverse.minimum;
assertEquals("binarySearch", toHexString(expected), toHexString(actual));
}
}
}
/**
* Tests the {@link CategoryList} constructor.
*/
@Test
public void testArgumentChecks() {
Category[] categories;
categories = new Category[] {
new Category("No data", null, 0),
new Category("Land", null, 10),
new Category("Clouds", null, 2),
new Category("Land again", null, 10) // Range overlaps.
};
try {
new CategoryList(categories, null);
fail("Argument check");
} catch (IllegalArgumentException exception) {
if (VERBOSE) {
System.out.println(exception.getLocalizedMessage());
// This is the expected exception.
}
}
for (int i=0; i<categories.length; i++) {
final Category cat = categories[i];
assertSame(cat, cat.geophysics(true).geophysics(false));
categories[i] = cat.geophysics(true);
}
try {
new CategoryList(categories, null);
fail("Argument check");
} catch (IllegalArgumentException exception) {
if (VERBOSE) {
System.out.println(exception.getLocalizedMessage());
// This is the expected exception.
}
}
// Removes the wrong category. Now, construction should succed.
categories = XArray.resize(categories, categories.length-1);
new CategoryList(categories, null);
}
/**
* Tests the {@link CategoryList#getCategory} method and a
* limited set of {@link CategoryList#transform} calls.
*
* @throws TransformException If an error occured while transforming a value.
*/
@Test
public void testGetCategory() throws TransformException {
final Category[] categories = new Category[] {
/*[0]*/ new Category("No data", null, 0),
/*[1]*/ new Category("Land", null, 7),
/*[2]*/ new Category("Clouds", null, 3),
/*[3]*/ new Category("Temperature", null, 10, 100, 0.1, 5),
/*[4]*/ new Category("Foo", null, 100, 120, -1, 3)
};
CategoryList list;
boolean searchNearest = false;
do {
list = new CategoryList(categories, null, searchNearest, null);
assertTrue("containsAll", list.containsAll(Arrays.asList(categories)));
assertSame(list.geophysics(true), list.inverse());
assertSame(list.geophysics(true).geophysics(false), list);
assertSame(list.geophysics(false), list);
final Range range = list.getRange();
assertEquals("min", 0, ((Number)range.getMinValue()).doubleValue(), 0);
assertEquals("max", 120, ((Number)range.getMaxValue()).doubleValue(), 0);
assertTrue ("min included", range.isMinIncluded() == true);
assertTrue ("max included", range.isMaxIncluded() == false);
/*
* Checks category search.
*/
assertSame( "0", list.getCategory( 0), categories[0]);
assertSame( "7", list.getCategory( 7), categories[1]);
assertSame( "3", list.getCategory( 3), categories[2]);
assertSame(" 10", list.getCategory( 10), categories[3]);
assertSame(" 50", list.getCategory( 50), categories[3]);
assertSame("100", list.getCategory(100), categories[4]);
assertSame("110", list.getCategory(110), categories[4]);
if (searchNearest) {
assertSame( "-1", list.getCategory( -1), categories[0]); // Nearest sample is 0.
assertSame( "2", list.getCategory( 2), categories[2]); // Nearest sample is 3.
assertSame( "4", list.getCategory( 4), categories[2]); // Nearest sample is 3.
assertSame( "9", list.getCategory( 9), categories[3]); // Nearest sample is 10.
assertSame("120", list.getCategory(120), categories[4]); // Nearest sample is 119
assertSame("200", list.getCategory(200), categories[4]); // Nearest sample is 119
} else {
assertNull( "-1", list.getCategory( -1));
assertNull( "2", list.getCategory( 2));
assertNull( "4", list.getCategory( 4));
assertNull( "9", list.getCategory( 9));
assertNull("120", list.getCategory(120));
assertNull("200", list.getCategory(200));
}
/*
* Checks transformations.
*/
assertTrue ( "0", Double.isNaN(list.transform(0)));
assertTrue ( "7", Double.isNaN(list.transform(7)));
assertTrue ( "3", Double.isNaN(list.transform(3)));
assertEquals( "10", 6, list.transform( 10), EPS);
assertEquals( "50", 10, list.transform( 50), EPS);
assertEquals("100", -97, list.transform(100), EPS);
assertEquals("110", -107, list.transform(110), EPS);
try {
assertEquals("9", searchNearest ? 6 : 5.9, list.transform(9), EPS);
if (!searchNearest) {
fail();
}
} catch (TransformException exception) {
if (searchNearest) {
throw exception;
}
}
} while ((searchNearest = !searchNearest) == true);
/*
* Test transformation using methods working on arrays.
* We assume that the 'transform(double)' version can
* be used as a reference.
*/
final double[] input = new double[512];
final double[] output0 = new double[input.length];
final double[] output1 = new double[input.length];
for (int i=0; i<input.length; i++) {
input [i] = random.nextInt(130)-5;
output0[i] = list.transform(input[i]);
}
list.transform(input, 0, output1, 0, input.length);
compare(output0, output1, EPS);
/*
* Test the transform using overlapping array.
*/
System.arraycopy(input, 0, output1, 3, input.length-3);
list.transform (output1, 3, output1, 0, input.length-3);
System.arraycopy(output0, input.length-3, output1, input.length-3, 3);
compare(output0, output1, EPS);
// Implementation will do the following transform in reverse direction.
System.arraycopy(input, 3, output1, 0, input.length-3);
list.transform (output1, 0, output1, 3, input.length-3);
System.arraycopy(output0, 0, output1, 0, 3);
compare(output0, output1, EPS);
// Test inverse transform
list.inverse().transform(output0, 0, output0, 0, output0.length);
for (int i=0; i<output0.length; i++) {
final double expected = input[i];
if (expected >= 10 && expected < 120) {
// Values outside this range have been clamped.
// They would usually not be equal.
assertEquals("inverse", expected, output0[i], EPS);
}
}
}
/**
* Compares two arrays. Special comparaison is performed for NaN values.
*/
static void compare(final double[] output0, final double[] output1, final double eps) {
assertEquals("length", output0.length, output1.length);
for (int i=0; i<output0.length; i++) {
final double expected = output0[i];
final double actual = output1[i];
final String name = "transform[" + i + ']';
if (Double.isNaN(expected)) {
final String hex1 = Integer.toHexString(Float.floatToRawIntBits((float) expected));
final String hex2 = Integer.toHexString(Float.floatToRawIntBits((float) actual));
assertEquals(name, hex1, hex2);
continue;
}
assertEquals(name, expected, actual, eps);
}
}
}