001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.referencing.operation.transform;
017:
018: // J2SE dependencies
019: import java.awt.geom.AffineTransform;
020: import java.util.Arrays;
021: import java.util.Random;
022:
023: // JUnit dependencies
024: import junit.framework.Test;
025: import junit.framework.TestCase;
026: import junit.framework.TestSuite;
027:
028: // OpenGIS dependencies
029: import org.opengis.parameter.ParameterValueGroup;
030: import org.opengis.referencing.FactoryException;
031: import org.opengis.referencing.crs.CoordinateReferenceSystem;
032: import org.opengis.referencing.operation.MathTransform;
033: import org.opengis.referencing.operation.MathTransform1D;
034: import org.opengis.referencing.operation.MathTransform2D;
035: import org.opengis.referencing.operation.Matrix;
036: import org.opengis.referencing.operation.NoninvertibleTransformException;
037: import org.opengis.referencing.operation.TransformException;
038: import org.opengis.geometry.DirectPosition;
039:
040: // Geotools dependencies
041: import org.geotools.geometry.DirectPosition1D;
042: import org.geotools.geometry.GeneralDirectPosition;
043: import org.geotools.referencing.ReferencingFactoryFinder;
044: import org.geotools.referencing.crs.DefaultGeographicCRS;
045: import org.geotools.referencing.operation.LinearTransform;
046: import org.geotools.referencing.operation.DefaultMathTransformFactory;
047: import org.geotools.referencing.operation.matrix.MatrixFactory;
048: import org.geotools.referencing.operation.matrix.GeneralMatrix;
049: import org.geotools.referencing.operation.matrix.XMatrix;
050:
051: /**
052: * Tests various classes of {@link MathTransform}, including {@link ConcatenatedTransform}.
053: * Actually, there is many {@link ConcatenatedTransform}, each optimized for special cases.
054: * This test tries to test a wide range of subclasses.
055: *
056: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/test/java/org/geotools/referencing/operation/transform/MathTransformTest.java $
057: * @version $Id: MathTransformTest.java 25262 2007-04-23 21:11:16Z desruisseaux $
058: * @author Martin Desruisseaux
059: */
060: public final class MathTransformTest extends TestCase {
061: /**
062: * Runs the tests with the textual test runner.
063: */
064: public static void main(String args[]) {
065: junit.textui.TestRunner.run(suite());
066: }
067:
068: /**
069: * Returns the test suite.
070: */
071: public static Test suite() {
072: return new TestSuite(MathTransformTest.class);
073: }
074:
075: /**
076: * Constructs a test case with the given name.
077: */
078: public MathTransformTest(final String name) {
079: super (name);
080: }
081:
082: /**
083: * Random numbers generator.
084: */
085: private Random random;
086:
087: /**
088: * The default math transform factory.
089: */
090: private DefaultMathTransformFactory factory;
091:
092: /**
093: * Expected accuracy for tests on JTS objects.
094: */
095: private static final double ACCURACY = 0.03;
096:
097: /**
098: * Set up common objects used for all tests.
099: */
100: protected void setUp() throws Exception {
101: super .setUp();
102: random = new Random(-3531834320875149028L);
103: factory = new DefaultMathTransformFactory();
104: }
105:
106: /**
107: * Tests a transformation on a {@link DirectPosition} object.
108: */
109: public void testDirectPositionTransform() throws FactoryException,
110: TransformException {
111: CoordinateReferenceSystem crs = ReferencingFactoryFinder
112: .getCRSFactory(null)
113: .createFromWKT(
114: "PROJCS[\"NAD_1983_UTM_Zone_10N\",\n"
115: + " GEOGCS[\"GCS_North_American_1983\",\n"
116: + " DATUM[\"D_North_American_1983\",\n"
117: + " TOWGS84[0,0,0,0,0,0,0]\n,"
118: + " SPHEROID[\"GRS_1980\", 6378137, 298.257222101]],\n"
119: + " PRIMEM[\"Greenwich\",0],\n"
120: + " UNIT[\"Degree\", 0.017453292519943295]],\n"
121: + " PROJECTION[\"Transverse_Mercator\"],\n"
122: + " PARAMETER[\"False_Easting\",500000],\n"
123: + " PARAMETER[\"False_Northing\",0],\n"
124: + " PARAMETER[\"Central_Meridian\",-123],\n"
125: + " PARAMETER[\"Scale_Factor\",0.9996],\n"
126: + " PARAMETER[\"Latitude_Of_Origin\",0],\n"
127: + " UNIT[\"Meter\",1]]");
128:
129: MathTransform t = ReferencingFactoryFinder
130: .getCoordinateOperationFactory(null).createOperation(
131: DefaultGeographicCRS.WGS84, crs)
132: .getMathTransform();
133: DirectPosition position = new GeneralDirectPosition(-123, 55);
134: position = t.transform(position, position);
135: position = t.inverse().transform(position, position);
136: assertEquals(-123, position.getOrdinate(0), 1E-6);
137: assertEquals(55, position.getOrdinate(1), 1E-6);
138: }
139:
140: /**
141: * Tests the {@link ProjectiveTransform} implementation.
142: */
143: public void testAffineTransform() throws FactoryException,
144: TransformException {
145: for (int pass = 0; pass < 10; pass++) {
146: final AffineTransform transform = new AffineTransform();
147: transform.rotate(Math.PI * random.nextDouble(),
148: 100 * random.nextDouble(), 100 * random
149: .nextDouble());
150: transform.scale(2 * random.nextDouble(), 2 * random
151: .nextDouble());
152: transform.shear(2 * random.nextDouble(), 2 * random
153: .nextDouble());
154: transform.translate(100 * random.nextDouble(), 100 * random
155: .nextDouble());
156: compareTransforms("AffineTransform", new MathTransform[] {
157: new ProjectiveTransform(
158: new GeneralMatrix(transform)),
159: new AffineTransform2D(transform) });
160: }
161:
162: AffineTransform at = new AffineTransform(23.157082917424454,
163: 0.0, 3220.1613428464952, 0.0, -23.157082917424457,
164: 1394.4593259871676);
165: MathTransform mt = factory
166: .createAffineTransform(new GeneralMatrix(at));
167:
168: final double[] points = new double[] { -129.992589135802,
169: 55.9226692948365, -129.987254340541, 55.9249676996729,
170: -129.982715772093, 55.9308988434656, -129.989772198265,
171: 55.9289277997662, -129.992589135802, 55.9226692948365 };
172: final double[] transformedPoints = new double[points.length];
173: mt
174: .transform(points, 0, transformedPoints, 0,
175: points.length / 2);
176: at.transform(points, 0, points, 0, points.length / 2);
177: for (int i = 0; i < transformedPoints.length; i++) {
178: assertEquals(points[i], transformedPoints[i], ACCURACY);
179: }
180: }
181:
182: /**
183: * Test various linear transformations. We test for many differents dimensions.
184: * The factory class should have created specialized classes for 1D and 2D cases.
185: * This test is used in order to ensure that specialized case produces the same
186: * results than general cases.
187: */
188: public void testSubAffineTransform() throws FactoryException,
189: TransformException {
190: for (int pass = 0; pass < 5; pass++) {
191: /*
192: * Construct the reference matrix.
193: */
194: final int dimension = 10;
195: final GeneralMatrix matrix = new GeneralMatrix(
196: dimension + 1, dimension + 1);
197: for (int i = 0; i < dimension; i++) {
198: matrix.setElement(i, i, 400 * Math.random() - 200);
199: matrix.setElement(i, dimension,
200: 400 * Math.random() - 200);
201: }
202: assertTrue(matrix.isAffine());
203: /*
204: * Construct all math transforms.
205: */
206: final MathTransform[] transforms = new MathTransform[dimension];
207: for (int i = 1; i <= dimension; i++) {
208: final GeneralMatrix sub = new GeneralMatrix(i + 1,
209: i + 1);
210: matrix.copySubMatrix(0, 0, i, i, 0, 0, sub); // Scale terms
211: matrix.copySubMatrix(0, dimension, i, 1, 0, i, sub); // Translation terms
212: final MathTransform transform = transforms[i - 1] = factory
213: .createAffineTransform(sub);
214: assertTrue(sub.isAffine());
215: assertEquals(sub, new GeneralMatrix(
216: ((LinearTransform) transform).getMatrix()));
217: assertInterfaced(transform);
218: assertTrue(i == transform.getSourceDimensions());
219: }
220: /*
221: * Check transformations and the inverse transformations.
222: */
223: assertTrue("MathTransform1D",
224: transforms[0] instanceof MathTransform1D);
225: assertTrue("MathTransform2D",
226: transforms[1] instanceof MathTransform2D);
227: assertEquals(matrix,
228: ((LinearTransform) transforms[dimension - 1])
229: .getMatrix());
230: compareTransforms("SubAffineTransform", transforms);
231: for (int i = 0; i < transforms.length; i++) {
232: transforms[i] = transforms[i].inverse();
233: }
234: compareTransforms("SubAffineTransform.inverse", transforms);
235: }
236: }
237:
238: /**
239: * Test the concatenation of linear transformations. Concatenation of linear
240: * transformations should involve only matrix multiplication. However, this
241: * test will also create {@link ConcatenatedTransform} objects in order to
242: * compare their results.
243: */
244: public void testAffineTransformConcatenation()
245: throws FactoryException, TransformException {
246: final MathTransform[] transforms = new MathTransform[2];
247: final int numDim = 4;
248: final int numPts = 200;
249: for (int pass = 0; pass < 100; pass++) {
250: final int dimSource = random.nextInt(numDim) + 1;
251: final int dimTarget = random.nextInt(numDim) + 1;
252: final int dimInterm = random.nextInt(numDim) + 1;
253: final Matrix matrix1 = getRandomMatrix(dimSource, dimInterm);
254: final Matrix matrix2 = getRandomMatrix(dimInterm, dimTarget);
255: final MathTransform tr1 = factory
256: .createAffineTransform(matrix1);
257: final MathTransform tr2 = factory
258: .createAffineTransform(matrix2);
259: final double[] sourcePt = new double[dimSource * numPts];
260: final double[] intermPt = new double[dimInterm * numPts];
261: final double[] targetPt = new double[dimTarget * numPts];
262: final double[] compare = new double[dimTarget * numPts];
263: final double[] delta = new double[dimTarget];
264: for (int i = 0; i < numPts; i++) {
265: sourcePt[i] = 100 * random.nextDouble() - 50;
266: }
267: tr1.transform(sourcePt, 0, intermPt, 0, numPts);
268: tr2.transform(intermPt, 0, targetPt, 0, numPts);
269: Arrays.fill(delta, 1E-6);
270: /*
271: * Create two set of concatenated transform: the first one computed from matrix
272: * multiplication; the second one is forced to a ConcatenatedTransform object
273: * for testing purpose.
274: */
275: transforms[0] = factory.createConcatenatedTransform(tr1,
276: tr2);
277: transforms[1] = ConcatenatedTransform
278: .createConcatenatedTransform(tr1, tr2);
279: assertTrue(transforms[0] instanceof LinearTransform);
280: assertFalse(transforms[1] instanceof LinearTransform);
281: for (int i = 0; i < transforms.length; i++) {
282: final MathTransform transform = transforms[i];
283: assertInterfaced(transform);
284: assertEquals("dimSource[" + i + ']', dimSource,
285: transform.getSourceDimensions());
286: assertEquals("dimTarget[" + i + ']', dimTarget,
287: transform.getTargetDimensions());
288: transform.transform(sourcePt, 0, compare, 0, numPts);
289: String name = "transform[" + i + "](" + dimSource
290: + " -> " + dimInterm + " -> " + dimTarget + ')';
291: assertPointsEqual(name, targetPt, compare, delta);
292: }
293: }
294: }
295:
296: /**
297: * Make sure that linear transformation preserve NaN values.
298: * This is required for {@link org.geotools.coverage.Category}.
299: */
300: public void testNaN() throws FactoryException, TransformException {
301: final XMatrix matrix = MatrixFactory.create(2);
302: matrix.setElement(0, 0, 0);
303: for (int i = 0; i < 200; i++) {
304: final int rawBits = 0x7FC00000 + random.nextInt(100);
305: final float value = Float.intBitsToFloat(rawBits);
306: assertTrue("isNaN", Float.isNaN(value));
307: matrix.setElement(0, 1, value);
308: final MathTransform1D tr = (MathTransform1D) factory
309: .createAffineTransform(matrix);
310: assertTrue("ConstantTransform1D",
311: tr instanceof ConstantTransform1D);
312: final float compare = (float) tr.transform(0);
313: assertEquals("rawBits", rawBits, Float
314: .floatToRawIntBits(compare));
315: }
316: }
317:
318: /**
319: * Test the {@link ExponentialTransform1D} and {@link LogarithmicTransform1D} classes
320: * using simple know values.
321: */
322: public void testLogarithmicTransform() throws FactoryException,
323: TransformException {
324: final double[] POWER_2 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
325: 11, 12, 13 };
326: final double[] VALUE_2 = { 1, 2, 4, 8, 16, 32, 64, 128, 256,
327: 512, 1024, 2048, 4096, 8192 };
328: final double[] POWER_10 = { -5, -4, -3, -2, -1, 0, +1, +2, +3,
329: +4, +5 };
330: final double[] VALUE_10 = { 1E-5, 1E-4, 1E-3, 1E-2, 1E-1, 1,
331: 1E+1, 1E+2, 1E+3, 1E+4, 1E+5 };
332: compareTransform1D("Exponential", 2, POWER_2, VALUE_2);
333: compareTransform1D("Exponential", 10, POWER_10, VALUE_10);
334: compareTransform1D("Logarithmic", 2, VALUE_2, POWER_2);
335: compareTransform1D("Logarithmic", 10, VALUE_10, POWER_10);
336: }
337:
338: /**
339: * Test the concatenation of {@link LinearTransform1D}, {@link ExponentialTransform1D}
340: * and {@link LogarithmicTransform1D}.
341: */
342: public void testLogarithmicAndExponentialConcatenation()
343: throws FactoryException, TransformException {
344: final int numPts = 200;
345: final double[] sourcePt = new double[numPts];
346: final double[] targetPt = new double[numPts];
347: final double[] compare = new double[numPts];
348: final double[] delta = new double[numPts];
349: for (int pass = 0; pass < 100; pass++) {
350: for (int i = 0; i < numPts; i++) {
351: sourcePt[i] = 20 * random.nextDouble() + 0.1;
352: }
353: MathTransform ctr = getRandomTransform1D();
354: ctr.transform(sourcePt, 0, targetPt, 0, numPts);
355: for (int i = random.nextInt(2) + 1; --i >= 0;) {
356: final MathTransform1D step = getRandomTransform1D();
357: ctr = (MathTransform1D) factory
358: .createConcatenatedTransform(ctr, step);
359: step.transform(targetPt, 0, targetPt, 0, numPts);
360: }
361: ctr.transform(sourcePt, 0, compare, 0, numPts);
362: final double EPS = Math.pow(10, -5 + countNonlinear(ctr));
363: for (int i = 0; i < numPts; i++) {
364: delta[i] = Math.max(1E-9, Math.abs(targetPt[i] * EPS));
365: if (targetPt[i] >= +1E+300)
366: targetPt[i] = Double.POSITIVE_INFINITY;
367: if (targetPt[i] <= -1E+300)
368: targetPt[i] = Double.NEGATIVE_INFINITY;
369: }
370: assertPointsEqual("transform[" + ctr + ']', targetPt,
371: compare, delta);
372: /*
373: * Test the inverse transform. It is difficult to get back the exact original value,
374: * since expressions like 'pow(b1, pow(b2,x))' tend to overflow or underflow very
375: * fast. We are very tolerant for this test because of this (exponential expression
376: * give exponential error). The 'testLogarithmicTransform' method tested the inverse
377: * transform in a more sever way.
378: */
379: try {
380: final MathTransform inv = ctr.inverse();
381: Arrays.fill(delta, Math.pow(10, countNonlinear(inv)));
382: inv.transform(targetPt, 0, compare, 0, numPts);
383: for (int i = 0; i < numPts; i++) {
384: if (!isReal(targetPt[i]) || !isReal(compare[i])) {
385: // Ignore all input points that produced NaN or infinity
386: // A succession of 2 "Exponentional" operation produces
387: // infinities pretty fast, so ignore it.
388: sourcePt[i] = Double.NaN;
389: }
390: }
391: assertPointsEqual("inverse[" + inv + ']', sourcePt,
392: compare, delta);
393: } catch (NoninvertibleTransformException exception) {
394: // Some transforms may not be invertible. Ignore...
395: }
396: }
397: }
398:
399: ///////////////////////////////////////////////////////////////////////////////////
400: //////////// ////////////
401: //////////// U T I L I T Y M E T H O D S ////////////
402: //////////// ////////////
403: ///////////////////////////////////////////////////////////////////////////////////
404:
405: /**
406: * Returns a random matrix.
407: *
408: * @param dimSource Number of dimension for input points.
409: * @param dimTarget Number of dimension for outout points.
410: */
411: private Matrix getRandomMatrix(final int dimSource,
412: final int dimTarget) {
413: final XMatrix matrix = MatrixFactory.create(dimTarget + 1,
414: dimSource + 1);
415: for (int j = 0; j < dimTarget; j++) { // Don't touch to the last row!
416: for (int i = 0; i <= dimSource; i++) {
417: matrix.setElement(j, i, 10 * random.nextDouble() - 5);
418: }
419: if (j <= dimSource) {
420: matrix.setElement(j, j, 40 * random.nextDouble() + 10);
421: }
422: matrix.setElement(j, dimSource,
423: 80 * random.nextDouble() - 40);
424: }
425: if (dimSource == dimTarget) {
426: assertTrue("Affine", matrix.isAffine());
427: }
428: return matrix;
429: }
430:
431: /**
432: * Gets a random one-dimensional transform.
433: */
434: private MathTransform1D getRandomTransform1D()
435: throws FactoryException {
436: final String[] candidates = { "Logarithmic", "Exponential",
437: "Affine" };
438: final String classification = candidates[random
439: .nextInt(candidates.length)];
440: final ParameterValueGroup parameters = factory
441: .getDefaultParameters(classification);
442: if (classification.equalsIgnoreCase("Affine")) {
443: parameters.parameter("num_row").setValue(2);
444: parameters.parameter("num_col").setValue(2);
445: parameters.parameter("elt_0_0").setValue(
446: random.nextDouble() * 2 + 0.1); // scale
447: parameters.parameter("elt_0_1").setValue(
448: random.nextDouble() * 1 - 2); // offset
449: } else {
450: parameters.parameter("base").setValue(
451: random.nextDouble() * 4 + 0.1);
452: }
453: return (MathTransform1D) factory
454: .createParameterizedTransform(parameters);
455: }
456:
457: /**
458: * Compare the transformation performed by many math transforms. If some transforms
459: * don't have the same number of input or output dimension, only the first input or
460: * output dimensions will be taken in account.
461: *
462: * @throws TransformException if a transformation failed.
463: */
464: private void compareTransforms(final String name,
465: final MathTransform[] transforms) throws TransformException {
466: /*
467: * Initialisation...
468: */
469: final GeneralDirectPosition[] sources = new GeneralDirectPosition[transforms.length];
470: final GeneralDirectPosition[] targets = new GeneralDirectPosition[transforms.length];
471: int maxDimSource = 0;
472: int maxDimTarget = 0;
473: for (int i = 0; i < transforms.length; i++) {
474: final int dimSource = transforms[i].getSourceDimensions();
475: final int dimTarget = transforms[i].getTargetDimensions();
476: if (dimSource > maxDimSource)
477: maxDimSource = dimSource;
478: if (dimTarget > maxDimTarget)
479: maxDimTarget = dimTarget;
480: sources[i] = new GeneralDirectPosition(dimSource);
481: targets[i] = new GeneralDirectPosition(dimTarget);
482: }
483: /*
484: * Test with an arbitrary number of randoms points.
485: */
486: for (int pass = 0; pass < 200; pass++) {
487: for (int j = 0; j < maxDimSource; j++) {
488: final double ord = 100 * random.nextDouble();
489: for (int i = 0; i < sources.length; i++) {
490: final GeneralDirectPosition source = sources[i];
491: if (j < source.ordinates.length) {
492: source.ordinates[j] = ord;
493: }
494: }
495: }
496: for (int j = 0; j < transforms.length; j++) {
497: assertSame(transforms[j].transform(sources[j],
498: targets[j]), targets[j]);
499: }
500: /*
501: * Compare all target points.
502: */
503: final StringBuffer buffer = new StringBuffer(name);
504: buffer.append(": Compare transform[");
505: final int lengthJ = buffer.length();
506: for (int j = 0; j < targets.length; j++) {
507: buffer.setLength(lengthJ);
508: buffer.append(j);
509: buffer.append("] with [");
510: final int lengthI = buffer.length();
511: final GeneralDirectPosition targetJ = targets[j];
512: for (int i = j + 1; i < targets.length; i++) {
513: buffer.setLength(lengthI);
514: buffer.append(i);
515: buffer.append(']');
516: final String label = buffer.toString();
517: final GeneralDirectPosition targetI = targets[i];
518: assertTrue(targetJ.ordinates != targetI.ordinates);
519: for (int k = Math.min(targetJ.ordinates.length,
520: targetI.ordinates.length); --k >= 0;) {
521: assertEquals(label, targetJ.ordinates[k],
522: targetI.ordinates[k], 1E-6);
523: }
524: }
525: }
526: }
527: }
528:
529: /**
530: * Compare the result of "Logarithmic" or "Exponential" transform with the expected results.
531: *
532: * @param classification The identifier name (e.g. "Exponential" or "Logarithmic").
533: * @param base The value for the "base" parameter.
534: * @param input Array of input values.
535: * @param expected Array of expected output values.
536: */
537: private void compareTransform1D(final String classification,
538: final double base, final double[] input,
539: final double[] expected) throws FactoryException,
540: TransformException {
541: assertEquals(input.length, expected.length);
542: final ParameterValueGroup parameters = factory
543: .getDefaultParameters(classification);
544: parameters.parameter("base").setValue(base);
545: final MathTransform1D direct = (MathTransform1D) factory
546: .createParameterizedTransform(parameters);
547: final MathTransform1D inverse = (MathTransform1D) direct
548: .inverse();
549: final DirectPosition1D point = new DirectPosition1D();
550: for (int i = 0; i < expected.length; i++) {
551: final double x = input[i];
552: final double y = direct.transform(x);
553: assertEquals("transform[x=" + x + ']', expected[i], y, 1E-6);
554: assertEquals("inverse [y=" + y + ']', x, inverse
555: .transform(y), 1E-6);
556: point.setOrdinate(0, x);
557: assertSame(direct.transform(point, point), point);
558: assertEquals(y, point.getOrdinate(0), 1E-9);
559: }
560: }
561:
562: /**
563: * Count the number of non-linear steps in a {@link MathTransform}.
564: */
565: private static int countNonlinear(final MathTransform transform) {
566: if ((transform instanceof ExponentialTransform1D)
567: || (transform instanceof LogarithmicTransform1D)) {
568: return 1;
569: }
570: if (transform instanceof ConcatenatedTransform) {
571: final ConcatenatedTransform ct = (ConcatenatedTransform) transform;
572: return countNonlinear(ct.transform1)
573: + countNonlinear(ct.transform2);
574: }
575: return 0;
576: }
577:
578: ///////////////////////////////////////////////////////////////////////////////////
579: //////////// ////////////
580: //////////// A S S E R T I O N M E T H O D S ////////////
581: //////////// ////////////
582: ///////////////////////////////////////////////////////////////////////////////////
583:
584: /**
585: * Returns {@code true} if the specified number is real (neither NaN or infinite).
586: */
587: private static boolean isReal(final double value) {
588: return !Double.isNaN(value) && !Double.isInfinite(value);
589: }
590:
591: /**
592: * Verify that the specified transform implements {@link MathTransform1D}
593: * or {@link MathTransform2D} as needed.
594: *
595: * @param transform The transform to test.
596: */
597: private static void assertInterfaced(final MathTransform transform) {
598: if (transform instanceof LinearTransform) {
599: final Matrix matrix = ((LinearTransform) transform)
600: .getMatrix();
601: if (!((XMatrix) matrix).isAffine()) {
602: // Special case: Non-affine transforms not yet declared as a 1D or 2D transform.
603: return;
604: }
605: }
606: int dim = transform.getSourceDimensions();
607: if (transform.getTargetDimensions() != dim) {
608: dim = 0;
609: }
610: assertTrue("MathTransform1D",
611: (dim == 1) == (transform instanceof MathTransform1D));
612: assertTrue("MathTransform2D",
613: (dim == 2) == (transform instanceof MathTransform2D));
614: }
615:
616: /**
617: * Compare two arrays of points.
618: *
619: * @param name The name of the comparaison to be performed.
620: * @param expected The expected array of points.
621: * @param actual The actual array of points.
622: * @param delta The maximal difference tolerated in comparaisons for each dimension.
623: * This array length must be equal to coordinate dimension (usually 1, 2 or 3).
624: */
625: private static void assertPointsEqual(final String name,
626: final double[] expected, final double[] actual,
627: final double[] delta) {
628: final int dimension = delta.length;
629: final int stop = Math.min(expected.length, actual.length)
630: / dimension * dimension;
631: assertEquals("Array length for expected points", stop,
632: expected.length);
633: assertEquals("Array length for actual points", stop,
634: actual.length);
635: final StringBuffer buffer = new StringBuffer(name);
636: buffer.append(": point[");
637: final int start = buffer.length();
638: for (int i = 0; i < stop; i++) {
639: buffer.setLength(start);
640: buffer.append(i / dimension);
641: buffer.append(", dimension ");
642: buffer.append(i % dimension);
643: buffer.append(" of ");
644: buffer.append(dimension);
645: buffer.append(']');
646: if (isReal(expected[i])) {
647: // The "two steps" method in ConcatenatedTransformTest sometime produces
648: // random NaN numbers. This "two steps" is used only for comparaison purpose;
649: // the "real" (tested) method work better.
650: assertEquals(buffer.toString(), expected[i], actual[i],
651: delta[i % dimension]);
652: }
653: }
654: }
655: }
|