/*
* 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.referencing.operation.builder;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.vecmath.MismatchedSizeException;
import junit.framework.Assert;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.referencing.crs.DefaultEngineeringCRS;
import org.geotools.referencing.operation.matrix.GeneralMatrix;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.junit.*;
import static org.junit.Assert.*;
/**
* A test for the MathTransformBuilders.
*
* @source $URL$
* @version $Id$
* @author Jan Jezek
* @author Adrian Custer
*/
public final class MathTransformBuilderTest {
/**
* Coordinates List generator.
*
* @param numberOfVertices count of generated points
* @param seed for random generating.
*
* @return points
*/
private List<MappedPosition> generateCoords(int numberOfVertices, long seed) {
CoordinateReferenceSystem crs = DefaultEngineeringCRS.CARTESIAN_2D;
return generateCoordsWithCRS(numberOfVertices, crs, seed, true);
}
/**
* Coordinates List generator.
*
* @param numberOfVertices count of generated points
* @param seed for random generating.
* @param includeAccuracy set true to generate points with accuracy.
*/
private List<MappedPosition> generateCoords(int numberOfVertices, long seed,
boolean includeAccuracy) {
CoordinateReferenceSystem crs = DefaultEngineeringCRS.CARTESIAN_2D;
return generateCoordsWithCRS(numberOfVertices, crs, seed, includeAccuracy);
}
/**
* Coordinates List generator.
*
* @param numberOfVertices count of generated points
* @param crs Coordinate Reference System of generated points
* @param seed seed for generate random numbers
* @param includeAccuracy set true to generate points with accuracy.
*/
private List <MappedPosition> generateCoordsWithCRS(int numberOfVertices,
CoordinateReferenceSystem crs, long seed, boolean includeAccuracy) {
List <MappedPosition>vert = new ArrayList<MappedPosition>();
Random randomCoord = new Random(seed);
for (int i = 0; i < numberOfVertices; i++) {
double xs = randomCoord.nextDouble() * 1000;
double ys = randomCoord.nextDouble() * 1000;
double xd = randomCoord.nextDouble() * 1000;
double yd = randomCoord.nextDouble() * 1000;
MappedPosition p = new MappedPosition(new DirectPosition2D(crs, xs,
ys), new DirectPosition2D(crs, xd, yd));
if (includeAccuracy) {
p.setAccuracy(randomCoord.nextDouble());
}
vert.add(p);
}
return vert;
}
/**
* Test expected values against transformed values.
*
* @param mt mathTransform that will be tested
* @param pts MappedPositions of source and target values.
*/
private void transformTest(MathTransform mt, List<MappedPosition> pts)
throws FactoryException, TransformException
{
double[] points = new double[pts.size() * 2];
double[] ptCalculated = new double[pts.size() * 2];
for (int i = 0; i < pts.size(); i++) {
points[(2 * i) ] = pts.get(i).getSource().getCoordinates()[0];
points[(2 * i) + 1] = pts.get(i).getSource().getCoordinates()[1];
}
mt.transform(points, 0, ptCalculated, 0, pts.size());
for (int i = 0; i < pts.size(); i++) {
assertEquals(pts.get(i).getTarget().getCoordinates()[0], ptCalculated[2 * i], 0.001);
assertEquals(pts.get(i).getTarget().getCoordinates()[1], ptCalculated[(2 * i) + 1], 0.001);
}
}
/**
* Test for {@linkplain RubberSheetBuilder RubberSheetBuilder}.
*/
@Test
public void testRubberBuilder() throws FactoryException, TransformException {
List<MappedPosition> pts = generateCoords(20, 8324);
CoordinateReferenceSystem crs = DefaultEngineeringCRS.CARTESIAN_2D;
List<DirectPosition> dpl = new ArrayList <DirectPosition>();
dpl.add( new DirectPosition2D(crs, 1000, 0) );
dpl.add( new DirectPosition2D(crs, 0, 0) );
dpl.add( new DirectPosition2D(crs, 0, 1000) );
dpl.add( new DirectPosition2D(crs, 1000, 1000) );
MathTransformBuilder ppc = new RubberSheetBuilder(pts, dpl);
transformTest(ppc.getMathTransform(), pts);
assertTrue(ppc.getErrorStatistics().rms() < 0.00001);
// Tests the formatting, but do not display to console for not polluting
// the output device. We just check that at least one axis title is
// there.
assertTrue(ppc.toString().indexOf(" x ") >= 0);
}
/**
* Test for {@linkplain ProjectiveTransformBuilder ProjectiveTransformBuilder}.
*/
@Test
public void testProjectiveBuilder() throws FactoryException, TransformException {
List<MappedPosition> pts = generateCoords(4, 312243);
MathTransformBuilder ppc = new ProjectiveTransformBuilder(pts);
transformTest(ppc.getMathTransform(), pts);
assertTrue(ppc.getErrorStatistics().rms() < 0.0001);
}
/**
* Test that all Matrixes are filled properly.
*/
@Test
public void testLSMCalculation() throws FactoryException, TransformException {
List<MappedPosition> pts = generateCoords(15, 3121123);
LSMTester buildTester = new LSMTester(pts);
buildTester.includeWeights(true);
buildTester.testLSM();
}
/**
* Test for {@linkplain AffineTransformBuilder AffineTransformBuilder}.
*/
@Test
public void testAffineBuilder() throws FactoryException, TransformException {
List<MappedPosition> pts = generateCoords(3, 2345);
MathTransformBuilder ppc = new AffineTransformBuilder(pts);
transformTest(ppc.getMathTransform(), pts);
assertTrue(ppc.getErrorStatistics().rms() < 0.00001);
}
/**
* Test for {@linkplain SimilarTransformBuilder SimilarTransformBuilder}.
*/
@Test
public void testSimilarBuilder() throws FactoryException, TransformException {
List<MappedPosition> pts = generateCoords(2, 24535);
MathTransformBuilder ppc = new SimilarTransformBuilder(pts);
transformTest(ppc.getMathTransform(), pts);
assertTrue(ppc.getErrorStatistics().rms() < 0.00001);
}
@Test
public void testAdvancedAffineBuilder(){
try {
List<MappedPosition> pts = generateCoords(3, 1245);
AdvancedAffineBuilder aab = new AdvancedAffineBuilder(pts);
aab.setConstrain(AdvancedAffineBuilder.SXY, 0);
AffineToGeometric a2g = new AffineToGeometric((AffineTransform2D)aab.getMathTransform());
Assert.assertEquals(a2g.getSkew(), 0, 0.000000001);
} catch (Exception e) {
fail(e.getMessage());
}
}
/**
* Test for MismatchedSizeException.
* @throws TransformException
*/
@Test(expected = MismatchedSizeException.class)
public void testMismatchedSizeException() throws TransformException {
// The exception should be thrown when the number of points is less than necessary
List<MappedPosition> pts = generateCoords(2, 2453655);
new AffineTransformBuilder(pts);
}
/**
* Test for MissingInfoException.
* @throws FactoryException
*/
@Test(expected = MissingInfoException.class)
public void testMissingInfoException() throws FactoryException {
// The exception should be thrown when the number of points is less than necessary
List <MappedPosition> pts = generateCoords(5, 2434765, false);
AffineTransformBuilder builder = new AffineTransformBuilder(pts);
builder.includeWeights(true);
}
/**
* Implements the method for testing the calculation of least
* square method. The main requirement of least square method is that
* A<sup>T<sup>PAx + A<sup>T<sup>PX = 0.
*
* @author jezekjas
*/
final class LSMTester extends ProjectiveTransformBuilder {
LSMTester(List <MappedPosition> pts) {
super(pts);
}
public void testLSM() {
// fill Matrix by calculateLSM()
this.calculateLSM();
GeneralMatrix AT = A.clone();
AT.transpose();
GeneralMatrix ATP = new GeneralMatrix(AT.getNumRow(), P.getNumCol());
GeneralMatrix ATPA = new GeneralMatrix(AT.getNumRow(), A.getNumCol());
GeneralMatrix ATPX = new GeneralMatrix(AT.getNumRow(), 1);
GeneralMatrix x = new GeneralMatrix(A.getNumCol(), 1);
ATP .mul(AT, P); // ATP
ATPA.mul(ATP, A); // ATPA
ATPX.mul(ATP, X); // ATPX
GeneralMatrix ATPAI = ATPA.clone();
ATPAI.invert();
x.mul(ATPAI, ATPX);
// assert the ATPAx + ATPX should be 0;.
x.mul(ATPA, x);
x.sub(ATPX);
double[] tx = new double[x.getNumRow()];
x.getColumn(0, tx);
for (int i = 0; i < tx.length; i++) {
assertTrue(tx[i] < 0.001);
}
}
}
}