001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    http://www.geoapi.org
004 *
005 *    Copyright (C) 2009-2019 Open Geospatial Consortium, Inc.
006 *    All Rights Reserved. http://www.opengeospatial.org/ogc/legal
007 *
008 *    Permission to use, copy, and modify this software and its documentation, with
009 *    or without modification, for any purpose and without fee or royalty is hereby
010 *    granted, provided that you include the following on ALL copies of the software
011 *    and documentation or portions thereof, including modifications, that you make:
012 *
013 *    1. The full text of this NOTICE in a location viewable to users of the
014 *       redistributed or derivative work.
015 *    2. Notice of any changes or modifications to the OGC files, including the
016 *       date changes were made.
017 *
018 *    THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE
019 *    NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
020 *    TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT
021 *    THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY
022 *    PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
023 *
024 *    COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
025 *    CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
026 *
027 *    The name and trademarks of copyright holders may NOT be used in advertising or
028 *    publicity pertaining to the software without specific, written prior permission.
029 *    Title to copyright in this software and any associated documentation will at all
030 *    times remain with copyright holders.
031 */
032package org.opengis.test.referencing;
033
034import java.util.Map;
035import java.util.List;
036import java.util.Collections;
037import javax.measure.Unit;
038import javax.measure.quantity.Angle;
039import javax.measure.quantity.Length;
040
041import org.opengis.parameter.*;
042import org.opengis.referencing.cs.*;
043import org.opengis.referencing.crs.*;
044import org.opengis.referencing.datum.*;
045import org.opengis.referencing.operation.*;
046import org.opengis.referencing.IdentifiedObject;
047import org.opengis.referencing.ObjectFactory;
048import org.opengis.util.Factory;
049import org.opengis.util.FactoryException;
050import org.opengis.test.Units;
051
052import org.junit.Test;
053import org.junit.runner.RunWith;
054import org.junit.runners.Parameterized;
055
056import static org.junit.Assume.*;
057import static org.opengis.test.Assert.*;
058import static org.opengis.referencing.cs.AxisDirection.*;
059
060
061/**
062 * Tests the creation of referencing objects from the {@linkplain ObjectFactory object factories}
063 * given at construction time.
064 *
065 * <div class="note"><b>Usage example:</b>
066 * in order to specify their factories and run the tests in a JUnit framework, implementors can
067 * define a subclass in their own test suite as in the example below:
068 *
069 * <blockquote><pre>import org.junit.runner.RunWith;
070 *import org.junit.runners.JUnit4;
071 *import org.opengis.test.referencing.ObjectFactoryTest;
072 *
073 *&#64;RunWith(JUnit4.class)
074 *public class MyTest extends ObjectFactoryTest {
075 *    public MyTest() {
076 *        super(new MyDatumFactory(), new MyCSFactory(), new MyCRSFactory(), new MyOpFactory());
077 *    }
078 *}</pre></blockquote>
079 * </div>
080 *
081 * @see AuthorityFactoryTest
082 * @see org.opengis.test.TestSuite
083 *
084 * @author  Cédric Briançon (Geomatys)
085 * @author  Martin Desruisseaux (Geomatys)
086 * @version 3.1
087 * @since   2.3
088 */
089@RunWith(Parameterized.class)
090public strictfp class ObjectFactoryTest extends ReferencingTestCase {
091    /**
092     * Factory to use for building {@link Datum} instances, or {@code null} if none.
093     */
094    protected final DatumFactory datumFactory;
095
096    /**
097     * Factory to use for building {@link CoordinateSystem} instances, or {@code null} if none.
098     */
099    protected final CSFactory csFactory;
100
101    /**
102     * Factory to use for building {@link CoordinateReferenceSystem} instances, or {@code null} if none.
103     */
104    protected final CRSFactory crsFactory;
105
106    /**
107     * Factory to use for building {@link Conversion} instances, or {@code null} if none.
108     */
109    protected final CoordinateOperationFactory copFactory;
110
111    /**
112     * Returns a default set of factories to use for running the tests. Those factories are given
113     * in arguments to the constructor when this test class is instantiated directly by JUnit (for
114     * example as a {@linkplain org.junit.runners.Suite.SuiteClasses suite} element), instead than
115     * subclassed by the implementor. The factories are fetched as documented in the
116     * {@link #factories(Class[])} javadoc.
117     *
118     * @return the default set of arguments to be given to the {@code ObjectFactoryTest} constructor.
119     *
120     * @since 3.1
121     */
122    @Parameterized.Parameters
123    @SuppressWarnings("unchecked")
124    public static List<Factory[]> factories() {
125        return factories(DatumFactory.class, CSFactory.class, CRSFactory.class, CoordinateOperationFactory.class);
126    }
127
128    /**
129     * Creates a new test using the given factories. If a given factory is {@code null},
130     * then the tests which depend on it will be skipped.
131     *
132     * @param datumFactory  factory for creating {@link Datum} instances.
133     * @param csFactory     factory for creating {@link CoordinateSystem} instances.
134     * @param crsFactory    factory for creating {@link CoordinateReferenceSystem} instances.
135     * @param copFactory    factory for creating {@link Conversion} instances.
136     */
137    public ObjectFactoryTest(
138            final DatumFactory             datumFactory,
139            final CSFactory                   csFactory,
140            final CRSFactory                 crsFactory,
141            final CoordinateOperationFactory copFactory)
142    {
143        super(datumFactory, csFactory, crsFactory, copFactory);
144        this.datumFactory = datumFactory;
145        this.csFactory    = csFactory;
146        this.crsFactory   = crsFactory;
147        this.copFactory   = copFactory;
148    }
149
150    /**
151     * Returns the authority factory tests backed by the object factories.
152     */
153    private AuthorityFactoryTest createAuthorityFactoryTest() {
154        final PseudoEpsgFactory factory = new PseudoEpsgFactory(Units.getDefault(),
155                datumFactory, csFactory, crsFactory, copFactory, null, validators);
156        return new AuthorityFactoryTest(factory, factory, factory);
157    }
158
159    /**
160     * Builds a map containing only one value, composed by the {@link IdentifiedObject#NAME_KEY}
161     * identifier and the value specified.
162     *
163     * @param  value  the value for the name key.
164     * @return a map containing only the value specified for the name key.
165     */
166    private static Map<String,String> name(final String value) {
167        return Collections.singletonMap(IdentifiedObject.NAME_KEY, value);
168    }
169
170    /**
171     * Tests the creation of the EPSG:4326 {@link GeographicCRS}. This method wraps the
172     * object factories in an {@link PseudoEpsgFactory} instance, then delegates to the
173     * {@link AuthorityFactoryTest#testWGS84()} method.
174     *
175     * @throws FactoryException if a factory fails to create a referencing object.
176     */
177    @Test
178    public void testWGS84() throws FactoryException {
179        createAuthorityFactoryTest().testWGS84();
180    }
181
182    /**
183     * Tests the creation of the WGS84 {@linkplain CoordinateReferenceSystem CRS} with ellipsoidal height, and
184     * verifies that the axes are in the given (<var>latitude</var>, <var>longitude</var>, <var>height</var>) order.
185     *
186     * @throws FactoryException if a factory fails to create a referencing object.
187     */
188    @Test
189    public void testWGS84_3D() throws FactoryException {
190        CoordinateSystemAxis λ, φ, h;
191        EllipsoidalCS cs;
192        GeographicCRS crs;              // The final product of this method.
193        GeodeticDatum datum;
194
195        final Unit<Length> metre = units.metre();
196        final Unit<Angle> degree = units.degree();
197
198        // Build a geodetic datum.
199        assumeNotNull(datumFactory);
200        validators.validate(datum = datumFactory.createGeodeticDatum(name("World Geodetic System 1984"),
201                                    datumFactory.createEllipsoid    (name("WGS 84"), 6378137.0, 298.257223563, metre),
202                                    datumFactory.createPrimeMeridian(name("Greenwich"), 0.0, degree)));
203
204        // Build an ellipsoidal coordinate system.
205        assumeNotNull(csFactory);
206        validators.validate(λ  = csFactory.createCoordinateSystemAxis(name("Geodetic longitude"), "λ", EAST,  degree));
207        validators.validate(φ  = csFactory.createCoordinateSystemAxis(name("Geodetic latitude"),  "φ", NORTH, degree));
208        validators.validate(h  = csFactory.createCoordinateSystemAxis(name("Ellipsoidal height"), "h", UP,    metre));
209        validators.validate(cs = csFactory.createEllipsoidalCS(name("WGS 84"), φ, λ, h));
210
211        // Finally build the geographic coordinate reference system.
212        assumeNotNull(crsFactory);
213        validators.validate(crs = crsFactory.createGeographicCRS(name("WGS84(DD)"), datum, cs));
214
215        datum = crs.getDatum();
216        verifyIdentification(datum, "World Geodetic System 1984", null);
217        verifyPrimeMeridian(datum.getPrimeMeridian(), "Greenwich", 0.0, degree);
218
219        cs = crs.getCoordinateSystem();
220        verifyCoordinateSystem(cs, EllipsoidalCS.class,
221                new AxisDirection[] {
222                    AxisDirection.NORTH,
223                    AxisDirection.EAST,
224                    AxisDirection.UP
225                }, degree, degree, metre);
226        verifyIdentification(cs.getAxis(0), "Geodetic latitude", null);
227        verifyIdentification(cs.getAxis(1), "Geodetic longitude", null);
228        verifyIdentification(cs.getAxis(2), "Ellipsoidal height", null);
229    }
230
231    /**
232     * Tests the creation of a geocentric CRS.
233     *
234     * @throws FactoryException if a factory fails to create a referencing object.
235     */
236    @Test
237    public void testGeocentric() throws FactoryException {
238        final CoordinateSystemAxis X, Y, Z;
239        final CartesianCS   cs;
240        final GeocentricCRS crs;            // The final product of this method.
241        final GeodeticDatum datum;
242        final PrimeMeridian greenwich;
243        final Ellipsoid     ellipsoid;
244
245        final Unit<Length> metre = units.metre();
246        final Unit<Angle> degree = units.degree();
247
248        assumeNotNull(datumFactory);
249        validators.validate(greenwich = datumFactory.createPrimeMeridian  (name("Greenwich Meridian"), 0, degree));
250        validators.validate(ellipsoid = datumFactory.createFlattenedSphere(name("WGS84 Ellipsoid"), 6378137, 298.257223563, metre));
251        validators.validate(datum     = datumFactory.createGeodeticDatum  (name("WGS84 Datum"), ellipsoid, greenwich));
252
253        assumeNotNull(csFactory);
254        validators.validate(X  = csFactory.createCoordinateSystemAxis(name("Geocentric X"), "X", GEOCENTRIC_X, metre));
255        validators.validate(Y  = csFactory.createCoordinateSystemAxis(name("Geocentric Y"), "Y", GEOCENTRIC_Y, metre));
256        validators.validate(Z  = csFactory.createCoordinateSystemAxis(name("Geocentric Z"), "Z", GEOCENTRIC_Z, metre));
257        validators.validate(cs = csFactory.createCartesianCS(name("Geocentric CS"), X, Z, Y));
258
259        assumeNotNull(crsFactory);
260        validators.validate(crs = crsFactory.createGeocentricCRS(name("Geocentric CRS"), datum, cs));
261        assertAxisDirectionsEqual("GeocentricCRS", crs.getCoordinateSystem(), GEOCENTRIC_X, GEOCENTRIC_Z, GEOCENTRIC_Y);
262    }
263
264    /**
265     * Tests the creation of a projected CRS with vertical height.
266     *
267     * @throws FactoryException if a factory fails to create a referencing object.
268     *
269     * @deprecated Renamed {@link #testProjectedWithGeoidalHeight()} for making clearer that this is not
270     *             a projected CRS associated to a 3D coordinate system.
271     */
272    @Test
273    @Deprecated
274    public void testProjected3D() throws FactoryException {
275        testProjectedWithGeoidalHeight();
276    }
277
278    /**
279     * Tests the creation of a compound CRS made of a projected CRS with a gravity-related height.
280     *
281     * @throws FactoryException if a factory fails to create a referencing object.
282     */
283    @Test
284    public void testProjectedWithGeoidalHeight() throws FactoryException {
285        final CoordinateSystemAxis axisN, axisE, axisH, axisφ, axisλ;
286
287        final EllipsoidalCS   baseCS;
288        final GeographicCRS   baseCRS;
289        final GeodeticDatum   baseDatum;
290        final PrimeMeridian   greenwich;
291        final Ellipsoid       ellipsoid;
292
293        final CartesianCS     projectedCS;
294        final ProjectedCRS    projectedCRS;
295        final OperationMethod projectionMethod;
296        final Conversion      baseToUTM;
297        final int             utmZone = 12;
298
299        final VerticalCS      heightCS;
300        final VerticalCRS     heightCRS;
301        final VerticalDatum   heightDatum;
302        final CompoundCRS     crs3D;            // The final product of this method.
303
304        final Unit<Length> metre = units.metre();
305        final Unit<Angle> degree = units.degree();
306
307        assumeNotNull(datumFactory);
308        validators.validate(greenwich   = datumFactory.createPrimeMeridian  (name("Greenwich Meridian"), 0, degree));
309        validators.validate(ellipsoid   = datumFactory.createFlattenedSphere(name("WGS84 Ellipsoid"), 6378137, 298.257223563, metre));
310        validators.validate(baseDatum   = datumFactory.createGeodeticDatum  (name("WGS84 Datum"), ellipsoid, greenwich));
311        validators.validate(heightDatum = datumFactory.createVerticalDatum  (name("WGS84 geoidal height"), VerticalDatumType.GEOIDAL));
312
313        assumeNotNull(csFactory);
314        validators.validate(axisN       = csFactory.createCoordinateSystemAxis(name("Northing"),               "N", NORTH, metre));
315        validators.validate(axisE       = csFactory.createCoordinateSystemAxis(name("Easting"),                "E", EAST,  metre));
316        validators.validate(axisH       = csFactory.createCoordinateSystemAxis(name("Gravity-related Height"), "H", UP,    metre));
317        validators.validate(axisφ       = csFactory.createCoordinateSystemAxis(name("Geodetic Latitude"),      "φ", NORTH, degree));
318        validators.validate(axisλ       = csFactory.createCoordinateSystemAxis(name("Geodetic Longitude"),     "λ", EAST,  degree));
319        validators.validate(baseCS      = csFactory.createEllipsoidalCS       (name("2D ellipsoidal"),  axisλ, axisφ));
320        validators.validate(projectedCS = csFactory.createCartesianCS         (name("2D Cartesian CS"), axisN, axisE));
321        validators.validate(heightCS    = csFactory.createVerticalCS          (name("Height CS"),       axisH));
322
323        assumeNotNull(crsFactory);
324        baseCRS   = crsFactory.createGeographicCRS(name("2D geographic CRS"), baseDatum, baseCS);
325        heightCRS = crsFactory.createVerticalCRS  (name("Height CRS"),      heightDatum, heightCS);
326
327        assumeNotNull(copFactory);
328        validators.validate(projectionMethod = copFactory.getOperationMethod("Transverse_Mercator"));
329        final ParameterValueGroup paramUTM = projectionMethod.getParameters().createValue();
330        paramUTM.parameter("central_meridian")  .setValue(-180 + utmZone*6 - 3);
331        paramUTM.parameter("latitude_of_origin").setValue(0.0);
332        paramUTM.parameter("scale_factor")      .setValue(0.9996);
333        paramUTM.parameter("false_easting")     .setValue(500000.0);
334        paramUTM.parameter("false_northing")    .setValue(0.0);
335        validators.validate(paramUTM);
336
337        validators.validate(baseToUTM    = copFactory .createDefiningConversion(name("Transverse_Mercator"), projectionMethod, paramUTM));
338        validators.validate(projectedCRS = crsFactory.createProjectedCRS(name("WGS 84 / UTM Zone 12 (2D)"), baseCRS, baseToUTM, projectedCS));
339        validators.validate(crs3D        = crsFactory.createCompoundCRS(name("3D Compound WGS 84 / UTM Zone 12"), projectedCRS, heightCRS));
340        assertAxisDirectionsEqual("CompoundCRS", crs3D.getCoordinateSystem(), NORTH, EAST, UP);
341    }
342}