001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2003, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.coverage.grid;
018:
019: // J2SE dependencies
020: import java.awt.Point;
021: import java.awt.Rectangle;
022: import java.awt.geom.AffineTransform;
023: import java.awt.geom.NoninvertibleTransformException;
024: import java.awt.image.RenderedImage;
025: import java.awt.image.renderable.ParameterBlock;
026:
027: // JAI dependencies
028: import javax.media.jai.JAI;
029: import javax.media.jai.RenderedOp;
030:
031: // JUnit dependencies
032: import junit.framework.Test;
033: import junit.framework.TestSuite;
034:
035: // OpenGIS dependencies
036: import org.opengis.parameter.ParameterValueGroup;
037: import org.opengis.referencing.FactoryException;
038: import org.opengis.referencing.NoSuchIdentifierException;
039: import org.opengis.referencing.crs.CoordinateReferenceSystem;
040: import org.opengis.referencing.crs.GeographicCRS;
041: import org.opengis.referencing.datum.Ellipsoid;
042: import org.opengis.referencing.datum.GeodeticDatum;
043: import org.opengis.referencing.operation.MathTransform;
044:
045: // Geotools dependencies
046: import org.geotools.referencing.CRS;
047: import org.geotools.referencing.cs.DefaultCartesianCS;
048: import org.geotools.referencing.crs.DefaultDerivedCRS;
049: import org.geotools.referencing.crs.DefaultProjectedCRS;
050: import org.geotools.referencing.operation.DefaultOperationMethod;
051: import org.geotools.referencing.operation.DefaultMathTransformFactory;
052: import org.geotools.referencing.operation.transform.ProjectiveTransform;
053: import org.geotools.coverage.processing.AbstractProcessor;
054: import org.geotools.coverage.processing.DefaultProcessor;
055: import org.geotools.coverage.processing.Operations;
056: import org.geotools.coverage.FactoryFinder;
057: import org.geotools.factory.Hints;
058:
059: /**
060: * Visual test of the "Resample" operation. A remote sensing image is projected from a fitted
061: * coordinate system to a geographic one.
062: *
063: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/test/java/org/geotools/coverage/grid/ResampleTest.java $
064: * @version $Id: ResampleTest.java 27848 2007-11-12 13:10:32Z desruisseaux $
065: * @author Remi Eve
066: * @author Martin Desruisseaux
067: */
068: public final class ResampleTest extends GridCoverageTest {
069: /**
070: * Set to {@code true} if the test case should show the projection results
071: * in a windows. This flag is set to {@code true} if the test is run from
072: * the command line through the {@code main(String[])} method. Otherwise
073: * (for example if it is run from Maven), it is left to {@code false}.
074: *
075: * @todo Consider setting to {@link TestData#isInteractive}.
076: */
077: private static boolean SHOW = false;
078:
079: /**
080: * Small number for comparaisons.
081: */
082: private static final double EPS = 1E-6;
083:
084: /**
085: * The source grid coverage, to be initialized by {@link #setUp}.
086: * Contains 8-bits indexed color model for a PNG image, with categories.
087: */
088: private GridCoverage2D coverage;
089:
090: /**
091: * An other source coverage initialized by {@link #setUp}.
092: * Contains indexed color model for a GIF image, without categories.
093: */
094: private GridCoverage2D indexedCoverage;
095:
096: /**
097: * An other source coverage initialized by {@link #setUp}.
098: * Contains indexed color model for a GIF image, without categories.
099: */
100: private GridCoverage2D indexedCoverageWithTransparency;
101:
102: /**
103: * An other source coverage initialized by {@link #setUp}.
104: * Contains float values.
105: */
106: private GridCoverage2D floatCoverage;
107:
108: /**
109: * Constructs a test case with the given name.
110: */
111: public ResampleTest(final String name) {
112: super (name);
113: }
114:
115: /**
116: * Set up common objects used for all tests.
117: */
118: protected void setUp() throws Exception {
119: super .setUp();
120: coverage = GridCoverageExamples.getExample(0);
121: indexedCoverage = GridCoverageExamples.getExample(2);
122: indexedCoverageWithTransparency = GridCoverageExamples
123: .getExample(3);
124: floatCoverage = GridCoverageExamples.getExample(4);
125: }
126:
127: /**
128: * Applies an operation on the specified coverage.
129: * This is invoked before to run the tests defined in the super-class.
130: */
131: //@Override
132: protected GridCoverage2D transform(final GridCoverage2D coverage) {
133: return (GridCoverage2D) Operations.DEFAULT.resample(coverage,
134: getProjectedCRS(coverage));
135: }
136:
137: /**
138: * Compares two affine transforms.
139: */
140: public static void assertEquals(final AffineTransform expected,
141: final AffineTransform actual) {
142: assertEquals("scaleX", expected.getScaleX(),
143: actual.getScaleX(), EPS);
144: assertEquals("scaleY", expected.getScaleY(),
145: actual.getScaleY(), EPS);
146: assertEquals("shearX", expected.getShearX(),
147: actual.getShearX(), EPS);
148: assertEquals("shearY", expected.getShearY(),
149: actual.getShearY(), EPS);
150: assertEquals("translateX", expected.getTranslateX(), actual
151: .getTranslateX(), EPS);
152: assertEquals("translateY", expected.getTranslateY(), actual
153: .getTranslateY(), EPS);
154: }
155:
156: /**
157: * Returns the "Sample to geophysics" transform as an affine transform.
158: */
159: private static AffineTransform getAffineTransform(
160: final GridCoverage2D coverage) {
161: AffineTransform tr;
162: tr = (AffineTransform) ((GridGeometry2D) coverage
163: .getGridGeometry()).getGridToCRS2D();
164: tr = new AffineTransform(tr); // Change the type to the default Java2D implementation.
165: return tr;
166: }
167:
168: /**
169: * Returns a projected CRS for test purpose.
170: */
171: private static CoordinateReferenceSystem getProjectedCRS(
172: final GridCoverage2D coverage) {
173: try {
174: final GeographicCRS base = (GeographicCRS) coverage
175: .getCoordinateReferenceSystem();
176: final Ellipsoid ellipsoid = ((GeodeticDatum) base
177: .getDatum()).getEllipsoid();
178: final DefaultMathTransformFactory factory = new DefaultMathTransformFactory();
179: final ParameterValueGroup parameters = factory
180: .getDefaultParameters("Oblique_Stereographic");
181: parameters.parameter("semi_major").setValue(
182: ellipsoid.getSemiMajorAxis());
183: parameters.parameter("semi_minor").setValue(
184: ellipsoid.getSemiMinorAxis());
185: parameters.parameter("central_meridian").setValue(5);
186: parameters.parameter("latitude_of_origin").setValue(-5);
187: final MathTransform mt;
188: try {
189: mt = factory.createParameterizedTransform(parameters);
190: } catch (FactoryException exception) {
191: fail(exception.getLocalizedMessage());
192: return null;
193: }
194: return new DefaultProjectedCRS("Stereographic",
195: new DefaultOperationMethod(mt), base, mt,
196: DefaultCartesianCS.PROJECTED);
197: } catch (NoSuchIdentifierException exception) {
198: fail(exception.getLocalizedMessage());
199: return null;
200: }
201: }
202:
203: /**
204: * Projects the specified image to the specified CRS.
205: * The result will be displayed in a window if {@link #SHOW} is set to {@code true}.
206: *
207: * @return The operation name which was applied on the image, or {@code null} if none.
208: */
209: private static String projectTo(final GridCoverage2D coverage,
210: final CoordinateReferenceSystem targetCRS,
211: final GridGeometry2D geometry) {
212: return projectTo(coverage, targetCRS, geometry, null, true);
213: }
214:
215: /**
216: * Projects the specified image to the specified CRS using the specified hints.
217: * The result will be displayed in a window if {@link #SHOW} is set to {@code true}.
218: *
219: * @return The operation name which was applied on the image, or {@code null} if none.
220: */
221: private static String projectTo(final GridCoverage2D coverage,
222: final CoordinateReferenceSystem targetCRS,
223: final GridGeometry2D geometry, final Hints hints,
224: final boolean useGeophysics) {
225: final AbstractProcessor processor = (hints != null) ? new DefaultProcessor(
226: hints)
227: : AbstractProcessor.getInstance();
228: final String arg1;
229: final Object value1;
230: final String arg2;
231: final Object value2;
232: if (targetCRS != null) {
233: arg1 = "CoordinateReferenceSystem";
234: value1 = targetCRS;
235: if (geometry != null) {
236: arg2 = "GridGeometry";
237: value2 = geometry;
238: } else {
239: arg2 = "InterpolationType";
240: value2 = "bilinear";
241: }
242: } else {
243: arg1 = "GridGeometry";
244: value1 = geometry;
245: arg2 = "InterpolationType";
246: value2 = "bilinear";
247: }
248: GridCoverage2D projected = coverage.geophysics(useGeophysics);
249: final ParameterValueGroup param = processor.getOperation(
250: "Resample").getParameters();
251: param.parameter("Source").setValue(projected);
252: param.parameter(arg1).setValue(value1);
253: param.parameter(arg2).setValue(value2);
254: projected = (GridCoverage2D) processor.doOperation(param);
255: final RenderedImage image = projected.getRenderedImage();
256: projected = projected.geophysics(false);
257: String operation = null;
258: if (image instanceof RenderedOp) {
259: operation = ((RenderedOp) image).getOperationName();
260: AbstractProcessor.LOGGER.fine("Applied \"" + operation
261: + "\" JAI operation.");
262: }
263: if (SHOW) {
264: Viewer.show(projected, operation);
265: } else {
266: // Force computation
267: assertNotNull(projected.getRenderedImage().getData());
268: }
269: return operation;
270: }
271:
272: /**
273: * Tests the "Resample" operation with an identity transform.
274: */
275: public void testIdentity() {
276: assertEquals("Lookup", projectTo(coverage, coverage
277: .getCoordinateReferenceSystem(), null));
278: assertNull(projectTo(indexedCoverage, indexedCoverage
279: .getCoordinateReferenceSystem(), null));
280: assertNull(projectTo(indexedCoverageWithTransparency,
281: indexedCoverageWithTransparency
282: .getCoordinateReferenceSystem(), null));
283: assertNull(projectTo(floatCoverage, floatCoverage
284: .getCoordinateReferenceSystem(), null));
285: }
286:
287: /**
288: * Tests the "Resample" operation with a "Crop" transform.
289: */
290: public void testCrop() {
291: assertEquals("Crop",
292: projectTo(coverage, null, new GridGeometry2D(
293: new GeneralGridRange(new Rectangle(50, 50, 200,
294: 200)), (MathTransform) null, null)));
295: assertEquals("Crop",
296: projectTo(indexedCoverage, null, new GridGeometry2D(
297: new GeneralGridRange(new Rectangle(50, 50, 100,
298: 100)), (MathTransform) null, null)));
299: assertEquals("Crop", projectTo(indexedCoverageWithTransparency,
300: null, new GridGeometry2D(new GeneralGridRange(
301: new Rectangle(50, 50, 100, 100)),
302: (MathTransform) null, null)));
303: assertEquals("Crop",
304: projectTo(floatCoverage, null, new GridGeometry2D(
305: new GeneralGridRange(new Rectangle(50, 50, 100,
306: 100)), (MathTransform) null, null),
307: new Hints(Hints.REPLACE_NON_GEOPHYSICS_VIEW,
308: Boolean.FALSE), false));
309: }
310:
311: /**
312: * Tests the "Resample" operation with a stereographic coordinate system.
313: */
314: public void testStereographic() {
315: assertEquals("Warp", projectTo(coverage,
316: getProjectedCRS(coverage), null));
317: }
318:
319: /**
320: * Tests the "Resample" operation with a stereographic coordinate system.
321: */
322: public void testsNad83() throws FactoryException {
323: final CoordinateReferenceSystem crs = CRS
324: .parseWKT("GEOGCS[\"NAD83\","
325: + "DATUM[\"North_American_Datum_1983\","
326: + "SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],"
327: + "TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6269\"]],"
328: + "PRIMEM[\"Greenwich\",0, AUTHORITY[\"EPSG\",\"8901\"]],"
329: + "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],"
330: + "AXIS[\"Lat\",NORTH],"
331: + "AXIS[\"Long\",EAST],"
332: + "AUTHORITY[\"EPSG\",\"4269\"]]");
333: assertEquals("Warp", projectTo(indexedCoverage, crs, null));
334: assertEquals("Warp", projectTo(indexedCoverageWithTransparency,
335: crs, null));
336: assertEquals("Warp", projectTo(floatCoverage, crs, null,
337: new Hints(Hints.REPLACE_NON_GEOPHYSICS_VIEW,
338: Boolean.FALSE), false));
339: }
340:
341: /**
342: * Tests the "Resample" operation with an "Affine" transform.
343: */
344: public void testAffine() {
345: performAffine(coverage, null, true, "Lookup", "Affine");
346: performAffine(indexedCoverage, null, true, "Lookup", "Affine");
347: performAffine(indexedCoverageWithTransparency, null, false,
348: "BandSelect", "Affine");
349: performAffine(floatCoverage, new Hints(
350: Hints.REPLACE_NON_GEOPHYSICS_VIEW, Boolean.FALSE),
351: false, "Lookup", "Affine");
352: }
353:
354: /**
355: * Performs an Affine transformation on the provided {@link GridCoverage2D} using the
356: * Resample operation.
357: *
358: * @param coverage the {@link GridCoverage2D} to apply the operation on.
359: */
360: private static void performAffine(final GridCoverage2D coverage,
361: final Hints hints, final boolean useGeophysics,
362: final String testString1, final String testString2) {
363: final AffineTransform atr = getAffineTransform(coverage);
364: atr.preConcatenate(AffineTransform.getTranslateInstance(5, 5));
365: final MathTransform tr = ProjectiveTransform.create(atr);
366: CoordinateReferenceSystem crs = coverage
367: .getCoordinateReferenceSystem();
368: crs = new DefaultDerivedCRS("F2",
369: new DefaultOperationMethod(tr), crs, tr, crs
370: .getCoordinateSystem());
371: /*
372: * Note: In current Resampler implementation, the affine transform effect tested
373: * on the first line below will not be visible with the simple viewer used
374: * here. It would be visible however with more elaborated viewer like the
375: * one provided in the {@code org.geotools.renderer} package.
376: */
377: String operation = projectTo(coverage, crs, null);
378: if (operation != null) {
379: // TODO
380: // assertEquals(testString1, operation);
381: }
382: operation = projectTo(coverage, null, new GridGeometry2D(null,
383: tr, null), hints, useGeophysics);
384: if (operation != null) {
385: // TODO
386: // assertEquals(testString2, operation);
387: }
388: }
389:
390: /**
391: * Tests <var>X</var>,<var>Y</var> translation in the {@link GridGeometry} after
392: * a "Resample" operation.
393: */
394: public void testTranslation()
395: throws NoninvertibleTransformException {
396: doTranslation(coverage);
397: doTranslation(indexedCoverage);
398: doTranslation(indexedCoverageWithTransparency);
399: }
400:
401: /**
402: * Performs a translation using the "Resample" operation.
403: *
404: * @param grid the {@link GridCoverage2D} to apply the translation on.
405: */
406: private void doTranslation(GridCoverage2D grid)
407: throws NoninvertibleTransformException {
408: final int transX = -253;
409: final int transY = -456;
410: final double scaleX = 0.04;
411: final double scaleY = -0.04;
412: final ParameterBlock block = new ParameterBlock().addSource(
413: grid.getRenderedImage()).add((float) transX).add(
414: (float) transY);
415: RenderedImage image = JAI.create("Translate", block);
416: assertEquals("Incorrect X translation", transX, image.getMinX());
417: assertEquals("Incorrect Y translation", transY, image.getMinY());
418: /*
419: * Create a grid coverage from the translated image but with the same envelope.
420: * Consequently, the 'gridToCoordinateSystem' should be translated by the same
421: * amount, with the opposite sign.
422: */
423: final AffineTransform expected = getAffineTransform(grid);
424: grid = (GridCoverage2D) FactoryFinder.getGridCoverageFactory(
425: null).create("Translated", image, grid.getEnvelope(),
426: grid.getSampleDimensions(),
427: new GridCoverage2D[] { grid }, grid.getProperties());
428: expected.translate(-transX, -transY);
429: assertEquals(expected, getAffineTransform(grid));
430: /*
431: * Apply the "Resample" operation with a specific 'gridToCoordinateSystem' transform.
432: * The envelope is left unchanged. The "Resample" operation should compute automatically
433: * new image bounds.
434: */
435: final AffineTransform at = AffineTransform.getScaleInstance(
436: scaleX, scaleY);
437: final MathTransform tr = ProjectiveTransform.create(at);
438: final GridGeometry2D geometry = new GridGeometry2D(null, tr,
439: null);
440: grid = (GridCoverage2D) Operations.DEFAULT.resample(grid, grid
441: .getCoordinateReferenceSystem(), geometry, null);
442: assertEquals(at, getAffineTransform(grid));
443: image = grid.getRenderedImage();
444: expected.preConcatenate(at.createInverse());
445: final Point point = new Point(transX, transY);
446: assertSame(point, expected.transform(point, point)); // Round toward neareast integer
447: assertEquals("Incorrect X translation", point.x, image
448: .getMinX());
449: assertEquals("Incorrect Y translation", point.y, image
450: .getMinY());
451: }
452:
453: /**
454: * Inherited test disabled for this suite.
455: *
456: * @todo Investigate why this test fails.
457: */
458: public void testSerialization() {
459: }
460:
461: /**
462: * Returns the test suite.
463: */
464: public static Test suite() {
465: return new TestSuite(ResampleTest.class);
466: }
467:
468: /**
469: * Run the suit from the command line.
470: */
471: public static void main(final String[] args) {
472: SHOW = true;
473: org.geotools.util.logging.Logging.GEOTOOLS
474: .forceMonolineConsoleOutput(AbstractProcessor.OPERATION);
475: junit.textui.TestRunner.run(suite());
476: }
477: }
|