001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    Copyright © 2009-2024 Open Geospatial Consortium, Inc.
004 *    http://www.geoapi.org
005 *
006 *    Licensed under the Apache License, Version 2.0 (the "License");
007 *    you may not use this file except in compliance with the License.
008 *    You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *    Unless required by applicable law or agreed to in writing, software
013 *    distributed under the License is distributed on an "AS IS" BASIS,
014 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 *    See the License for the specific language governing permissions and
016 *    limitations under the License.
017 */
018package org.opengis.test.referencing;
019
020import java.util.Map;
021import java.util.Collections;
022import javax.measure.Unit;
023import javax.measure.quantity.Angle;
024import javax.measure.quantity.Length;
025
026import org.opengis.parameter.*;
027import org.opengis.referencing.cs.*;
028import org.opengis.referencing.crs.*;
029import org.opengis.referencing.datum.*;
030import org.opengis.referencing.operation.*;
031import org.opengis.referencing.IdentifiedObject;
032import org.opengis.referencing.ObjectFactory;
033import org.opengis.util.FactoryException;
034import org.opengis.test.Units;
035
036import org.junit.jupiter.api.Test;
037
038import static org.junit.jupiter.api.Assumptions.assumeTrue;
039import static org.opengis.test.Assertions.assertAxisDirectionsEqual;
040import static org.opengis.referencing.cs.AxisDirection.*;
041
042
043/**
044 * Tests the creation of referencing objects from the {@linkplain ObjectFactory object factories}
045 * given at construction time.
046 *
047 * <h2>Usage example:</h2>
048 * in order to specify their factories and run the tests in a JUnit framework, implementers can
049 * define a subclass in their own test suite as in the example below:
050 *
051 * {@snippet lang="java" :
052 * import org.opengis.test.referencing.ObjectFactoryTest;
053 *
054 * public class MyTest extends ObjectFactoryTest {
055 *     public MyTest() {
056 *         super(new MyDatumFactory(), new MyCSFactory(), new MyCRSFactory(), new MyOpFactory());
057 *     }
058 * }}
059 *
060 * @see AuthorityFactoryTest
061 *
062 * @author  Cédric Briançon (Geomatys)
063 * @author  Martin Desruisseaux (Geomatys)
064 * @version 3.1
065 * @since   2.3
066 */
067@SuppressWarnings("strictfp")   // Because we still target Java 11.
068public strictfp class ObjectFactoryTest extends ReferencingTestCase {
069    /**
070     * The message when a test is disabled because no factory has been found.
071     */
072    static final String NO_CRS_FACTORY   = "No Coordinate Reference System (CRS) factory found.",
073                        NO_CS_FACTORY    = "No Coordinate System (CS) factory found.",
074                        NO_DATUM_FACTORY = "No Datum factory found.",
075                        NO_COP_FACTORY   = "No Coordinate Operation factory found.";
076
077    /**
078     * Factory to use for building {@link Datum} instances, or {@code null} if none.
079     */
080    protected final DatumFactory datumFactory;
081
082    /**
083     * Factory to use for building {@link CoordinateSystem} instances, or {@code null} if none.
084     */
085    protected final CSFactory csFactory;
086
087    /**
088     * Factory to use for building {@link CoordinateReferenceSystem} instances, or {@code null} if none.
089     */
090    protected final CRSFactory crsFactory;
091
092    /**
093     * Factory to use for building {@link Conversion} instances, or {@code null} if none.
094     */
095    protected final CoordinateOperationFactory copFactory;
096
097    /**
098     * Creates a new test using the given factories. If a given factory is {@code null},
099     * then the tests which depend on it will be skipped.
100     *
101     * @param datumFactory  factory for creating {@link Datum} instances.
102     * @param csFactory     factory for creating {@link CoordinateSystem} instances.
103     * @param crsFactory    factory for creating {@link CoordinateReferenceSystem} instances.
104     * @param copFactory    factory for creating {@link Conversion} instances.
105     */
106    public ObjectFactoryTest(
107            final DatumFactory             datumFactory,
108            final CSFactory                   csFactory,
109            final CRSFactory                 crsFactory,
110            final CoordinateOperationFactory copFactory)
111    {
112        this.datumFactory = datumFactory;
113        this.csFactory    = csFactory;
114        this.crsFactory   = crsFactory;
115        this.copFactory   = copFactory;
116    }
117
118    /**
119     * {@return the authority factory tests backed by the object factories}.
120     */
121    private AuthorityFactoryTest createAuthorityFactoryTest() {
122        final PseudoEpsgFactory factory = new PseudoEpsgFactory(Units.getDefault(),
123                datumFactory, csFactory, crsFactory, copFactory, null, validators);
124        return new AuthorityFactoryTest(factory, factory, factory);
125    }
126
127    /**
128     * Builds a map containing only one value, composed by the {@link IdentifiedObject#NAME_KEY}
129     * identifier and the value specified.
130     *
131     * @param  value  the value for the name key.
132     * @return a map containing only the value specified for the name key.
133     */
134    private static Map<String,String> name(final String value) {
135        return Collections.singletonMap(IdentifiedObject.NAME_KEY, value);
136    }
137
138    /**
139     * Tests the creation of the EPSG:4326 {@link GeographicCRS}. This method wraps the
140     * object factories in an {@link PseudoEpsgFactory} instance, then delegates to the
141     * {@link AuthorityFactoryTest#testWGS84()} method.
142     *
143     * @throws FactoryException if a factory fails to create a referencing object.
144     */
145    @Test
146    public void testWGS84() throws FactoryException {
147        createAuthorityFactoryTest().testWGS84();
148    }
149
150    /**
151     * Tests the creation of the WGS84 {@linkplain CoordinateReferenceSystem CRS} with ellipsoidal height, and
152     * verifies that the axes are in the given (<var>latitude</var>, <var>longitude</var>, <var>height</var>) order.
153     *
154     * @throws FactoryException if a factory fails to create a referencing object.
155     */
156    @Test
157    public void testWGS84_3D() throws FactoryException {
158        CoordinateSystemAxis λ, φ, h;
159        EllipsoidalCS cs;
160        GeographicCRS crs;              // The final product of this method.
161        GeodeticDatum datum;
162
163        final Unit<Length> metre = units.metre();
164        final Unit<Angle> degree = units.degree();
165
166        // Build a geodetic reference frame.
167        assumeTrue(datumFactory != null, NO_DATUM_FACTORY);
168        validators.validate(datum = datumFactory.createGeodeticDatum(name("World Geodetic System 1984"),
169                                    datumFactory.createEllipsoid    (name("WGS 84"), 6378137.0, 298.257223563, metre),
170                                    datumFactory.createPrimeMeridian(name("Greenwich"), 0.0, degree)));
171
172        // Build an ellipsoidal coordinate system.
173        assumeTrue(csFactory != null, NO_CS_FACTORY);
174        validators.validate(λ  = csFactory.createCoordinateSystemAxis(name("Geodetic longitude"), "λ", EAST,  degree));
175        validators.validate(φ  = csFactory.createCoordinateSystemAxis(name("Geodetic latitude"),  "φ", NORTH, degree));
176        validators.validate(h  = csFactory.createCoordinateSystemAxis(name("Ellipsoidal height"), "h", UP,    metre));
177        validators.validate(cs = csFactory.createEllipsoidalCS(name("WGS 84"), φ, λ, h));
178
179        // Finally build the geographic coordinate reference system.
180        assumeTrue(crsFactory != null, NO_CRS_FACTORY);
181        validators.validate(crs = crsFactory.createGeographicCRS(name("WGS84(DD)"), datum, cs));
182
183        datum = crs.getDatum();
184        verifyIdentification(datum, "World Geodetic System 1984", null);
185        verifyPrimeMeridian(datum.getPrimeMeridian(), "Greenwich", 0.0, degree);
186
187        cs = crs.getCoordinateSystem();
188        verifyCoordinateSystem(cs, EllipsoidalCS.class,
189                new AxisDirection[] {
190                    AxisDirection.NORTH,
191                    AxisDirection.EAST,
192                    AxisDirection.UP
193                }, degree, degree, metre);
194        verifyIdentification(cs.getAxis(0), "Geodetic latitude", null);
195        verifyIdentification(cs.getAxis(1), "Geodetic longitude", null);
196        verifyIdentification(cs.getAxis(2), "Ellipsoidal height", null);
197    }
198
199    /**
200     * Tests the creation of a geocentric CRS.
201     *
202     * @throws FactoryException if a factory fails to create a referencing object.
203     */
204    @Test
205    public void testGeocentric() throws FactoryException {
206        final CoordinateSystemAxis X, Y, Z;
207        final CartesianCS cs;
208        final GeodeticCRS crs;              // The final product of this method.
209        final GeodeticDatum datum;
210        final PrimeMeridian greenwich;
211        final Ellipsoid     ellipsoid;
212
213        final Unit<Length> metre = units.metre();
214        final Unit<Angle> degree = units.degree();
215
216        assumeTrue(datumFactory != null, NO_DATUM_FACTORY);
217        validators.validate(greenwich = datumFactory.createPrimeMeridian  (name("Greenwich Meridian"), 0, degree));
218        validators.validate(ellipsoid = datumFactory.createFlattenedSphere(name("WGS84 Ellipsoid"), 6378137, 298.257223563, metre));
219        validators.validate(datum     = datumFactory.createGeodeticDatum  (name("WGS84 Datum"), ellipsoid, greenwich));
220
221        assumeTrue(csFactory != null, NO_CS_FACTORY);
222        validators.validate(X  = csFactory.createCoordinateSystemAxis(name("Geocentric X"), "X", GEOCENTRIC_X, metre));
223        validators.validate(Y  = csFactory.createCoordinateSystemAxis(name("Geocentric Y"), "Y", GEOCENTRIC_Y, metre));
224        validators.validate(Z  = csFactory.createCoordinateSystemAxis(name("Geocentric Z"), "Z", GEOCENTRIC_Z, metre));
225        validators.validate(cs = csFactory.createCartesianCS(name("Geocentric CS"), X, Z, Y));
226
227        assumeTrue(crsFactory != null, NO_CRS_FACTORY);
228        validators.validate(crs = crsFactory.createGeodeticCRS(name("Geocentric CRS"), datum, null, cs));
229        assertAxisDirectionsEqual(crs.getCoordinateSystem(), new AxisDirection[] {GEOCENTRIC_X, GEOCENTRIC_Z, GEOCENTRIC_Y}, "GeodeticCRS");
230    }
231
232    /**
233     * Tests the creation of a projected CRS with vertical height.
234     *
235     * @throws FactoryException if a factory fails to create a referencing object.
236     *
237     * @deprecated Renamed {@link #testProjectedWithGeoidalHeight()} for making clearer that this is not
238     *             a projected CRS associated to a 3D coordinate system.
239     */
240    @Test
241    @Deprecated(since="3.1", forRemoval=true)
242    public void testProjected3D() throws FactoryException {
243        testProjectedWithGeoidalHeight();
244    }
245
246    /**
247     * Tests the creation of a compound CRS made of a projected CRS with a gravity-related height.
248     *
249     * @throws FactoryException if a factory fails to create a referencing object.
250     */
251    @Test
252    public void testProjectedWithGeoidalHeight() throws FactoryException {
253        final CoordinateSystemAxis axisN, axisE, axisH, axisφ, axisλ;
254
255        final EllipsoidalCS   baseCS;
256        final GeographicCRS   baseCRS;
257        final GeodeticDatum   baseDatum;
258        final PrimeMeridian   greenwich;
259        final Ellipsoid       ellipsoid;
260
261        final CartesianCS     projectedCS;
262        final ProjectedCRS    projectedCRS;
263        final OperationMethod projectionMethod;
264        final Conversion      baseToUTM;
265        final int             utmZone = 12;
266
267        final VerticalCS      heightCS;
268        final VerticalCRS     heightCRS;
269        final VerticalDatum   heightDatum;
270        final CompoundCRS     crs3D;            // The final product of this method.
271
272        final Unit<Length> metre = units.metre();
273        final Unit<Angle> degree = units.degree();
274
275        assumeTrue(datumFactory != null, NO_DATUM_FACTORY);
276        validators.validate(greenwich   = datumFactory.createPrimeMeridian  (name("Greenwich Meridian"), 0, degree));
277        validators.validate(ellipsoid   = datumFactory.createFlattenedSphere(name("WGS84 Ellipsoid"), 6378137, 298.257223563, metre));
278        validators.validate(baseDatum   = datumFactory.createGeodeticDatum  (name("WGS84 Datum"), ellipsoid, greenwich));
279        validators.validate(heightDatum = datumFactory.createVerticalDatum  (name("WGS84 geoidal height"), RealizationMethod.GEOID));
280
281        assumeTrue(csFactory != null, NO_CS_FACTORY);
282        validators.validate(axisN       = csFactory.createCoordinateSystemAxis(name("Northing"),               "N", NORTH, metre));
283        validators.validate(axisE       = csFactory.createCoordinateSystemAxis(name("Easting"),                "E", EAST,  metre));
284        validators.validate(axisH       = csFactory.createCoordinateSystemAxis(name("Gravity-related Height"), "H", UP,    metre));
285        validators.validate(axisφ       = csFactory.createCoordinateSystemAxis(name("Geodetic Latitude"),      "φ", NORTH, degree));
286        validators.validate(axisλ       = csFactory.createCoordinateSystemAxis(name("Geodetic Longitude"),     "λ", EAST,  degree));
287        validators.validate(baseCS      = csFactory.createEllipsoidalCS       (name("2D ellipsoidal"),  axisλ, axisφ));
288        validators.validate(projectedCS = csFactory.createCartesianCS         (name("2D Cartesian CS"), axisN, axisE));
289        validators.validate(heightCS    = csFactory.createVerticalCS          (name("Height CS"),       axisH));
290
291        assumeTrue(crsFactory != null, NO_CRS_FACTORY);
292        baseCRS   = crsFactory.createGeographicCRS(name("2D geographic CRS"), baseDatum, baseCS);
293        heightCRS = crsFactory.createVerticalCRS  (name("Height CRS"),      heightDatum, heightCS);
294
295        assumeTrue(copFactory != null, NO_COP_FACTORY);
296        validators.validate(projectionMethod = copFactory.getOperationMethod("Transverse_Mercator"));
297        final ParameterValueGroup paramUTM = projectionMethod.getParameters().createValue();
298        paramUTM.parameter("central_meridian")  .setValue(-180 + utmZone*6 - 3);
299        paramUTM.parameter("latitude_of_origin").setValue(0.0);
300        paramUTM.parameter("scale_factor")      .setValue(0.9996);
301        paramUTM.parameter("false_easting")     .setValue(500000.0);
302        paramUTM.parameter("false_northing")    .setValue(0.0);
303        validators.validate(paramUTM);
304
305        validators.validate(baseToUTM    = copFactory .createDefiningConversion(name("Transverse_Mercator"), projectionMethod, paramUTM));
306        validators.validate(projectedCRS = crsFactory.createProjectedCRS(name("WGS 84 / UTM Zone 12 (2D)"), baseCRS, baseToUTM, projectedCS));
307        validators.validate(crs3D        = crsFactory.createCompoundCRS(name("3D Compound WGS 84 / UTM Zone 12"), projectedCRS, heightCRS));
308        assertAxisDirectionsEqual(crs3D.getCoordinateSystem(), new AxisDirection[] {NORTH, EAST, UP}, "CompoundCRS");
309    }
310}