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.Set;
035import java.util.List;
036import java.awt.geom.Rectangle2D;
037
038import org.opengis.util.Factory;
039import org.opengis.util.FactoryException;
040import org.opengis.referencing.cs.*;
041import org.opengis.referencing.crs.*;
042import org.opengis.referencing.datum.*;
043import org.opengis.referencing.IdentifiedObject;
044import org.opengis.referencing.AuthorityFactory;
045import org.opengis.referencing.NoSuchAuthorityCodeException;
046import org.opengis.referencing.operation.TransformException;
047import org.opengis.referencing.operation.MathTransform;
048import org.opengis.referencing.operation.Conversion;
049import org.opengis.metadata.extent.Extent;
050import org.opengis.metadata.extent.GeographicExtent;
051import org.opengis.metadata.extent.GeographicBoundingBox;
052import org.opengis.test.CalculationType;
053import org.opengis.test.ToleranceModifier;
054import org.opengis.test.Configuration;
055
056import org.junit.Test;
057import org.junit.runner.RunWith;
058import org.junit.runners.Parameterized;
059
060import static org.junit.Assume.*;
061import static org.opengis.test.Assert.*;
062import static org.opengis.test.Validator.DEFAULT_TOLERANCE;
063
064
065/**
066 * Tests the creation of referencing objects from the {@linkplain AuthorityFactory authority
067 * factories} given at construction time.
068 *
069 * <p>Many {@link ProjectedCRS} instances tested in this class use the same projections than the
070 * {@link MathTransform} instances tested in {@link ParameterizedTransformTest}. However the later
071 * test class expects (λ,φ) input ordinates in degrees and (<var>x</var>,<var>y</var>)
072 * output ordinates in metres, while this {@code AuthorityFactoryTest} class expects input and
073 * output ordinates in CRS-dependent units and axis order.</p>
074 *
075 * <div class="note"><b>Usage example:</b>
076 * in order to specify their factories and run the tests in a JUnit framework, implementors can
077 * define a subclass in their own test suite as in the example below:
078 *
079 * <blockquote><pre>import org.junit.runner.RunWith;
080 *import org.junit.runners.JUnit4;
081 *import org.opengis.test.referencing.AuthorityFactoryTest;
082 *
083 *&#64;RunWith(JUnit4.class)
084 *public class MyTest extends AuthorityFactoryTest {
085 *    public MyTest() {
086 *        super(new MyCRSAuthorityFactory(), new MyCSAuthorityFactory(), new MyDatumAuthorityFactory());
087 *    }
088 *}</pre></blockquote>
089 * </div>
090 *
091 * @see ObjectFactoryTest
092 * @see ParameterizedTransformTest
093 * @see org.opengis.test.TestSuite
094 *
095 * @author  Cédric Briançon (Geomatys)
096 * @author  Martin Desruisseaux (Geomatys)
097 * @version 3.1
098 * @since   2.3
099 */
100@RunWith(Parameterized.class)
101public strictfp class AuthorityFactoryTest extends ReferencingTestCase {
102    /**
103     * Factory to use for building {@link CoordinateReferenceSystem} instances, or {@code null} if none.
104     */
105    protected final CRSAuthorityFactory crsAuthorityFactory;
106
107    /**
108     * Factory to use for building {@link CoordinateSystem} instances, or {@code null} if none.
109     */
110    protected final CSAuthorityFactory csAuthorityFactory;
111
112    /**
113     * Factory to use for building {@link Datum} instances, or {@code null} if none.
114     */
115    protected final DatumAuthorityFactory datumAuthorityFactory;
116
117    /**
118     * The identified object (typically a {@link CoordinateReferenceSystem}) being tested.
119     * Every test methods in this class will set this field to a non-null value.
120     * Implementors can use this value for their own assertions after any test has been run.
121     *
122     * @since 3.1
123     */
124    protected IdentifiedObject object;
125
126    /**
127     * {@code true} if the longitude and latitude axes shall be swapped. This flag applies
128     * only to geographic coordinates.
129     *
130     * <p><b>Default value:</b> {@code true}, since the majority of {@link GeographicCRS}
131     * defined in the EPSG database uses the (φλ) axis order.</p>
132     *
133     * @since 3.1
134     */
135    protected boolean swapλφ = true;
136
137    /**
138     * {@code true} if the easting and northing axes shall be swapped. This flag applies only
139     * to projected coordinates.
140     *
141     * <p><b>Default value:</b> {@code false}, since the majority of {@link ProjectedCRS} defined
142     * in the EPSG database uses the (<var>x</var>,<var>y</var>) axis order.</p>
143     *
144     * @since 3.1
145     */
146    protected boolean swapxy = false;
147
148    /**
149     * {@code true} if the sign of ordinate values shall be reversed in both projected axes.
150     * This flag applies only to projected coordinates. This flag is set to {@code true} for
151     * <cite>South Oriented</cite> {@link ProjectedCRS}.
152     *
153     * <p><b>Default value:</b> {@code false}.</p>
154     *
155     * @since 3.1
156     */
157    private boolean flipxy = false;
158
159    /**
160     * {@code true} if the {@linkplain CoordinateReferenceSystem Coordinate Reference System} being
161     * tested is a polar CRS. Such CRS have axis orientation like <cite>"South along 90°E"</cite>
162     * instead than {@linkplain AxisDirection#EAST East} or {@linkplain AxisDirection#NORTH North}.
163     *
164     * <p><b>Default value:</b> {@code false}.</p>
165     *
166     * @since 3.1
167     */
168    private boolean isPolar = false;
169
170    /**
171     * The expected prime meridian of the CRS being tested, in decimal degrees from Greenwich.
172     *
173     * <p><b>Default value:</b> {@code 0.0}.</p>
174     *
175     * @since 3.1
176     */
177    protected double primeMeridian = 0.0;
178
179    /**
180     * Conversion factor from degrees to the CRS-specific angular units. This value is different
181     * than one when the latitude or longitude angles need to be converted from degrees before to
182     * run a test.
183     *
184     * <p><b>Default value:</b> {@code 1.0}.</p>
185     *
186     * @since 3.1
187     */
188    protected double toAngularUnit = 1.0;
189
190    /**
191     * Conversion factor from metres to the CRS-specific linear units. This value is different
192     * than one when the easting or northing values need to be converted from metres before to
193     * run a test.
194     *
195     * <p><b>Default value:</b> {@code 1.0}.</p>
196     *
197     * @since 3.1
198     */
199    protected double toLinearUnit = 1.0;
200
201    /**
202     * {@code true} if {@link #crsAuthorityFactory} and {@link #csAuthorityFactory} supports the
203     * creation of coordinate system with (<var>y</var>,<var>x</var>) axis order. If this field is
204     * set to {@code false}, then the tests that would normally expect (<var>y</var>,<var>x</var>)
205     * axis order or <cite>South Oriented</cite> CRS will rather use the (<var>x</var>,<var>y</var>)
206     * axis order and <cite>North Oriented</cite> CRS in their test.
207     *
208     * @since 3.1
209     */
210    protected boolean isAxisSwappingSupported;
211
212    /**
213     * A helper class to use for running the tests. The {@link #runProjectionTest(int)} method
214     * will use the following {@code ParameterizedTransformTest} package-private methods:
215     *
216     * <ul>
217     *   <li>{@link ParameterizedTransformTest#verifyKnownSamplePoints}</li>
218     *   <li>{@link ParameterizedTransformTest#verifyInDomainOfValidity}</li>
219     * </ul>
220     */
221    private final ParameterizedTransformTest test;
222
223    /**
224     * Returns a default set of factories to use for running the tests. Those factories are given
225     * in arguments to the constructor when this test class is instantiated directly by JUnit (for
226     * example as a {@linkplain org.junit.runners.Suite.SuiteClasses suite} element), instead than
227     * subclassed by the implementor. The factories are fetched as documented in the
228     * {@link #factories(Class[])} javadoc.
229     *
230     * @return the default set of arguments to be given to the {@code AuthorityFactoryTest} constructor.
231     *
232     * @since 3.1
233     */
234    @Parameterized.Parameters
235    @SuppressWarnings("unchecked")
236    public static List<Factory[]> factories() {
237        return factories(CRSAuthorityFactory.class, CSAuthorityFactory.class, DatumAuthorityFactory.class);
238    }
239
240    /**
241     * Creates a new test using the given factories. If a given factory is {@code null},
242     * then the tests which depend on it will be skipped.
243     *
244     * @param crsFactory    factory for creating {@link CoordinateReferenceSystem} instances.
245     * @param csFactory     factory for creating {@link CoordinateSystem} instances.
246     * @param datumFactory  factory for creating {@link Datum} instances.
247     */
248    public AuthorityFactoryTest(final CRSAuthorityFactory crsFactory,
249            final CSAuthorityFactory csFactory, final DatumAuthorityFactory datumFactory)
250    {
251        super(crsFactory, csFactory, datumFactory);
252        crsAuthorityFactory   = crsFactory;
253        csAuthorityFactory    = csFactory;
254        datumAuthorityFactory = datumFactory;
255        final Configuration.Key<Boolean>[] keys = ParameterizedTransformTest.getEnabledKeys(1);
256        final int offset = keys.length - 1;                     // First free slot for our keys.
257        keys[offset] = Configuration.Key.isAxisSwappingSupported;
258        final boolean[] isEnabled = getEnabledFlags(keys);
259        test = new ParameterizedTransformTest(isEnabled);
260        isAxisSwappingSupported = isEnabled[offset];
261    }
262
263    /**
264     * Returns information about the configuration of the test which has been run.
265     * This method returns a map containing:
266     *
267     * <ul>
268     *   <li>All the entries defined in the {@link ParameterizedTransformTest#configuration()
269     *       ParameterizedTransformTest} class except {@code mtFactory}.</li>
270     *   <li>All the following values associated to the {@link org.opengis.test.Configuration.Key} of the same name:
271     *     <ul>
272     *       <li>{@link #isAxisSwappingSupported}</li>
273     *       <li>{@link #crsAuthorityFactory}</li>
274     *       <li>{@link #csAuthorityFactory}</li>
275     *       <li>{@link #datumAuthorityFactory}</li>
276     *     </ul>
277     *   </li>
278     * </ul>
279     *
280     * @return the configuration of the test being run, or an empty map if none.
281     *
282     * @since 3.1
283     */
284    @Override
285    public Configuration configuration() {
286        final Configuration op = test.configuration();
287        assertNull(op.remove(Configuration.Key.mtFactory));
288        assertNull(op.put(Configuration.Key.isAxisSwappingSupported, isAxisSwappingSupported));
289        assertNull(op.put(Configuration.Key.crsAuthorityFactory,     crsAuthorityFactory));
290        assertNull(op.put(Configuration.Key.csAuthorityFactory,      csAuthorityFactory));
291        assertNull(op.put(Configuration.Key.datumAuthorityFactory,   datumAuthorityFactory));
292        return op;
293    }
294
295    /**
296     * Returns the longitude value relative to the Greenwich Meridian, expressed in decimal degrees.
297     *
298     * @param  pm  the prime meridian from which to get the Greenwich longitude, or {@code null}.
299     * @return the prime meridian in the given units, or 0 if the given prime meridian was null.
300     */
301    private double getGreenwichLongitude(final PrimeMeridian pm) {
302        return (pm != null) ? pm.getAngularUnit().getConverterTo(units.degree()).convert(pm.getGreenwichLongitude()) : 0;
303    }
304
305    /**
306     * Tests the creation of the EPSG:4326 {@link GeographicCRS}.
307     *
308     * @throws NoSuchAuthorityCodeException if the specified code is not found among the ones present in the database.
309     * @throws FactoryException if the creation of the {@link CoordinateReferenceSystem} failed for an other raison.
310     */
311    @Test
312    public void testWGS84() throws NoSuchAuthorityCodeException, FactoryException {
313        assumeNotNull(crsAuthorityFactory);
314        final GeographicCRS crs = crsAuthorityFactory.createGeographicCRS("EPSG:4326");
315        assertNotNull("CRSAuthorityFactory.createGeographicCRS()", crs);
316        object = crs;
317        validators.validate(crs);
318        verifyIdentification(crs, "WGS 84", null);
319        /*
320         * Coordinate system validation. In theory, the coordinate system is mandatory.
321         * This is verified by the above call to validate(crs). However the user could
322         * have set the Validator.requireMandatoryAttributes to 'false', in which case
323         * we need to be lenient as the user wish.
324         */
325        final EllipsoidalCS cs = crs.getCoordinateSystem();
326        if (cs != null) {
327            verifyCoordinateSystem(cs, EllipsoidalCS.class,
328                    new AxisDirection[] {
329                        AxisDirection.NORTH,
330                        AxisDirection.EAST
331                    }, units.degree());
332            verifyIdentification(cs.getAxis(0), "Geodetic latitude", null);
333            verifyIdentification(cs.getAxis(1), "Geodetic longitude", null);
334        }
335        /*
336         * Datum validation. Same rational about 'null' value as for the coordinate system.
337         */
338        final GeodeticDatum datum = crs.getDatum();
339        if (datum != null) {
340            verifyIdentification(datum, "World Geodetic System 1984", null);
341            verifyPrimeMeridian(datum.getPrimeMeridian(), "Greenwich", 0.0, units.degree());
342        }
343    }
344
345    /**
346     * Verifies the horizontal axis direction of the given coordinate system. The standard
347     * directions are (East,North), but the boolean argument allows to swap and flip those
348     * directions.
349     *
350     * @param cs    the coordinate system to check, or {@code null}.
351     * @param swap  {@code true} if the the easting and northing axes should be interchanged.
352     * @param flip  {@code true} if the sign of both axes should be reversed.
353     */
354    private static void verifyAxisDirection(final String message, final CoordinateSystem cs,
355            final boolean swap, final boolean flip)
356    {
357        if (cs != null) {
358            AxisDirection X,Y;
359            if (flip) {
360                X = AxisDirection.WEST;
361                Y = AxisDirection.SOUTH;
362            } else {
363                X = AxisDirection.EAST;
364                Y = AxisDirection.NORTH;
365            }
366            if (swap) {
367                final AxisDirection t = X;
368                X=Y; Y=t;
369            }
370            assertAxisDirectionsEqual(message, cs, X, Y);
371        }
372    }
373
374    /**
375     * Creates a {@link ProjectedCRS} identified by the given EPSG code, and tests its
376     * math transform. The set of allowed codes is documented in second column of the
377     * {@link PseudoEpsgFactory#createParameters(int)} method.
378     *
379     * @param  code  the EPSG code of a target Coordinate Reference System.
380     * @throws FactoryException if the math transform can not be created.
381     * @throws TransformException if a point can not be transformed.
382     */
383    private void runProjectionTest(final int code) throws FactoryException, TransformException {
384        if (!isAxisSwappingSupported) {
385            swapλφ = swapxy = flipxy = false;
386        }
387        assumeNotNull(crsAuthorityFactory);
388        final ProjectedCRS crs;
389        try {
390            crs = crsAuthorityFactory.createProjectedCRS("EPSG:" + code);
391        } catch (NoSuchAuthorityCodeException e) {
392            // If a code was not found, ensure that the factory does not declare that it was
393            // a supported code. If the code was unsupported, then the test will be ignored.
394            final Set<String> authorityCodes = crsAuthorityFactory.getAuthorityCodes(ProjectedCRS.class);
395            if (authorityCodes == null || !authorityCodes.contains(String.valueOf(code))) {
396                assumeNoException(e);               // Will mark the test as "ignored".
397            }
398            throw e;                                // Will mark the test as "failed".
399        }
400        assertNotNull("CRSAuthorityFactory.createProjectedCRS()", crs);
401        object = crs;
402        validators.validate(crs);
403        /*
404         * Coordinate system validation. In theory, the coordinate system is mandatory.
405         * This is verified by the above call to validate(crs). However the user could
406         * have set the Validator.requireMandatoryAttributes to 'false', in which case
407         * we need to be lenient as the user wishes.
408         */
409        final GeographicCRS baseCRS = crs.getBaseCRS();
410        if (baseCRS != null) {
411            verifyAxisDirection("BaseCRS", baseCRS.getCoordinateSystem(), swapλφ, false);
412            final GeodeticDatum datum = baseCRS.getDatum();
413            if (datum != null) {
414                assertEquals("PrimeMeridian.greenwichLongitude", primeMeridian,
415                        getGreenwichLongitude(datum.getPrimeMeridian()), DEFAULT_TOLERANCE);
416            }
417        }
418        /*
419         * Verifies axis direction only if the CRS is not polar. Polar CRS has unusual
420         * axis directions like "South along 90°E". Since there is no GeoAPI code list
421         * for such direction, we consider them as implementation-dependent.
422         */
423        if (!isPolar) {
424            verifyAxisDirection("ProjectedCRS", crs.getCoordinateSystem(), swapxy, flipxy);
425        }
426        /*
427         * Test the projection of sample point values.
428         */
429        final Conversion conversion = crs.getConversionFromBase();
430        if (conversion != null) {
431            final MathTransform projection = conversion.getMathTransform();
432            if (projection != null) {
433                test.description = Utilities.getName(crs);
434                test.transform = projection;
435                validators.validate(projection);
436                /*
437                 * Get the sample points and swap ordinate values if needed.
438                 * Finally apply a unit conversion if the CRS doesn't use the usual units.
439                 */
440                final SamplePoints sample = SamplePoints.forCRS(code);
441                if (primeMeridian != 0) sample.rotateLongitude(primeMeridian);
442                if (swapλφ) SamplePoints.swap(sample.sourcePoints);
443                if (swapxy) SamplePoints.swap(sample.targetPoints);
444                if (flipxy) SamplePoints.flip(sample.targetPoints);
445                test.setTolerance(swapλφ ? ToleranceModifier.PROJECTION_FROM_φλ : ToleranceModifier.PROJECTION);
446                if (toLinearUnit  != 1) test.applyUnitConversion(CalculationType.DIRECT_TRANSFORM,  sample.targetPoints, toLinearUnit);
447                if (toAngularUnit != 1) test.applyUnitConversion(CalculationType.INVERSE_TRANSFORM, sample.sourcePoints, toAngularUnit);
448                test.verifyTransform(sample.sourcePoints, sample.targetPoints);
449                /*
450                 * Tests random points in every domains of validity declared in the CRS.
451                 * If the CRS does not declare any domain of validity, then we will use
452                 * the one which is hard-coded in the SamplePoints class.
453                 */
454                boolean tested = false;
455                final Rectangle2D areaOfValidity = sample.areaOfValidity;
456                double λmin = areaOfValidity.getMinX();
457                double λmax = areaOfValidity.getMaxX();
458                double φmin = areaOfValidity.getMinY();
459                double φmax = areaOfValidity.getMaxY();
460                final Extent extent = crs.getDomainOfValidity();
461                validators.validate(extent);
462                if (extent != null) {
463                    for (final GeographicExtent element : extent.getGeographicElements()) {
464                        if (element instanceof GeographicBoundingBox && Boolean.TRUE.equals(element.getInclusion())) {
465                            final GeographicBoundingBox bbox = (GeographicBoundingBox) element;
466                            λmin = bbox.getWestBoundLongitude();
467                            λmax = bbox.getEastBoundLongitude();
468                            φmin = bbox.getSouthBoundLatitude();
469                            φmax = bbox.getNorthBoundLatitude();
470                            setRect(areaOfValidity, λmin, φmin, λmax, φmax, swapλφ, toAngularUnit);
471                            assertFalse("Empty geographic bounding box.", areaOfValidity.isEmpty());
472                            test.verifyInDomainOfValidity(areaOfValidity);
473                            tested = true;
474                        }
475                    }
476                }
477                if (!tested) {
478                    setRect(areaOfValidity, λmin, φmin, λmax, φmax, swapλφ, toAngularUnit);
479                    test.verifyInDomainOfValidity(areaOfValidity);
480                }
481            }
482        }
483    }
484
485    /**
486     * Sets the area of validity, swapping axis and converting units if necessary.
487     */
488    private static void setRect(final Rectangle2D areaOfValidity,
489            double λmin, double φmin, double λmax, double φmax,
490            final boolean swapλφ, final double toAngularUnit)
491    {
492        λmin *= toAngularUnit;
493        λmax *= toAngularUnit;
494        φmin *= toAngularUnit;
495        φmax *= toAngularUnit;
496        if (swapλφ) {
497            double t;
498            t=λmin; λmin=φmin; φmin=t;
499            t=λmax; λmax=φmax; φmax=t;
500        }
501        areaOfValidity.setRect(λmin, φmin, λmax-λmin, φmax-φmin);
502    }
503
504    /**
505     * Tests the EPSG:3002 (<cite>Makassar / NEIEZ</cite>) projected CRS.
506     *
507     * <table class="ogc">
508     * <caption>CRS characteristics</caption>
509     * <tr><td>Projection method:</td> <td>Mercator (variant A)</td></tr>
510     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
511     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
512     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in metres</td></tr>
513     * </table>
514     *
515     * @throws FactoryException if the math transform can not be created.
516     * @throws TransformException if the example point can not be transformed.
517     *
518     * @see ParameterizedTransformTest#testMercator1SP()
519     */
520    @Test
521    public void testEPSG_3002() throws FactoryException, TransformException {
522        runProjectionTest(3002);
523    }
524
525    /**
526     * Tests the EPSG:3388 (<cite>Pulkovo 1942 / Caspian Sea Mercator</cite>) projected CRS.
527     *
528     * <table class="ogc">
529     * <caption>CRS characteristics</caption>
530     * <tr><td>Projection method:</td> <td>Mercator (variant B)</td></tr>
531     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
532     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
533     * <tr><td>Output ordinates:</td>  <td>(<var>y</var>,<var>x</var>) in metres - <strong>note the axis order!</strong></td></tr>
534     * </table>
535     *
536     * @throws FactoryException if the math transform can not be created.
537     * @throws TransformException if the example point can not be transformed.
538     *
539     * @see ParameterizedTransformTest#testMercator2SP()
540     */
541    @Test
542    public void testEPSG_3388() throws FactoryException, TransformException {
543        swapxy = true;
544        runProjectionTest(3388);
545    }
546
547    /**
548     * Tests the EPSG:3857 (<cite>WGS 84 / Pseudo-Mercator</cite>) projected CRS.
549     *
550     * <table class="ogc">
551     * <caption>CRS characteristics</caption>
552     * <tr><td>Projection method:</td> <td>Mercator Popular Visualisation Pseudo Mercator</td></tr>
553     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
554     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
555     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in metres</td></tr>
556     * </table>
557     *
558     * @throws FactoryException if the math transform can not be created.
559     * @throws TransformException if the example point can not be transformed.
560     *
561     * @see ParameterizedTransformTest#testPseudoMercator()
562     */
563    @Test
564    public void testEPSG_3857() throws FactoryException, TransformException {
565        runProjectionTest(3857);
566    }
567
568    /**
569     * Tests the IGNF:MILLER (unofficial EPSG:310642901 code) projected CRS.
570     *
571     * <table class="ogc">
572     * <caption>CRS characteristics</caption>
573     * <tr><td>Projection method:</td> <td>Miller</td></tr>
574     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
575     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
576     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in metres</td></tr>
577     * </table>
578     *
579     * @throws FactoryException if the math transform can not be created.
580     * @throws TransformException if the example point can not be transformed.
581     *
582     * @see ParameterizedTransformTest#testMiller()
583     */
584    @Test
585    public void testIGNF_MILLER() throws FactoryException, TransformException {
586        runProjectionTest(310642901);
587    }
588
589    /**
590     * Tests the EPSG:29873 (<cite>Timbalai 1948 / RSO Borneo (m)</cite>) projected CRS.
591     *
592     * <table class="ogc">
593     * <caption>CRS characteristics</caption>
594     * <tr><td>Projection method:</td> <td>Hotine Oblique Mercator (variant B)</td></tr>
595     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
596     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
597     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in metres</td></tr>
598     * </table>
599     *
600     * @throws FactoryException if the math transform can not be created.
601     * @throws TransformException if the example point can not be transformed.
602     *
603     * @see ParameterizedTransformTest#testHotineObliqueMercator()
604     */
605    @Test
606    public void testEPSG_29873() throws FactoryException, TransformException {
607        runProjectionTest(29873);
608    }
609
610    /**
611     * Tests the EPSG:27700 (<cite>OSGB 1936 / British National Grid</cite>) projected CRS.
612     *
613     * <table class="ogc">
614     * <caption>CRS characteristics</caption>
615     * <tr><td>Projection method:</td> <td>Transverse Mercator</td></tr>
616     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
617     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
618     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in metres</td></tr>
619     * </table>
620     *
621     * @throws FactoryException if the math transform can not be created.
622     * @throws TransformException if the example point can not be transformed.
623     *
624     * @see ParameterizedTransformTest#testTransverseMercator()
625     */
626    @Test
627    public void testEPSG_27700() throws FactoryException, TransformException {
628        runProjectionTest(27700);
629    }
630
631    /**
632     * Tests the EPSG:2314 (<cite>Trinidad 1903 / Trinidad Grid</cite>) projected CRS.
633     *
634     * <table class="ogc">
635     * <caption>CRS characteristics</caption>
636     * <tr><td>Projection method:</td> <td>Cassini-Soldner</td></tr>
637     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
638     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
639     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in Clarke's foot - <strong>note the units!</strong></td></tr>
640     * </table>
641     *
642     * @throws FactoryException if the math transform can not be created.
643     * @throws TransformException if the example point can not be transformed.
644     *
645     * @see ParameterizedTransformTest#testCassiniSoldner()
646     */
647    @Test
648    public void testEPSG_2314() throws FactoryException, TransformException {
649        toLinearUnit = 1/PseudoEpsgFactory.CLARKE_KEET;
650        runProjectionTest(2314);
651    }
652
653    /**
654     * Tests the EPSG:24200 (<cite>JAD69 / Jamaica National Grid</cite>) projected CRS.
655     *
656     * <table class="ogc">
657     * <caption>CRS characteristics</caption>
658     * <tr><td>Projection method:</td> <td>Lambert Conic Conformal (1SP)</td></tr>
659     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
660     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
661     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in metres</td></tr>
662     * </table>
663     *
664     * @throws FactoryException if the math transform can not be created.
665     * @throws TransformException if the example point can not be transformed.
666     *
667     * @see ParameterizedTransformTest#testLambertConicConformal1SP()
668     */
669    @Test
670    public void testEPSG_24200() throws FactoryException, TransformException {
671        runProjectionTest(24200);
672    }
673
674    /**
675     * Tests the EPSG:32040 (<cite>NAD27 / Texas South Central</cite>) projected CRS.
676     *
677     * <table class="ogc">
678     * <caption>CRS characteristics</caption>
679     * <tr><td>Projection method:</td> <td>Lambert Conic Conformal (2SP)</td></tr>
680     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
681     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
682     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in US feet - <strong>note the units!</strong></td></tr>
683     * </table>
684     *
685     * @throws FactoryException if the math transform can not be created.
686     * @throws TransformException if the example point can not be transformed.
687     *
688     * @see ParameterizedTransformTest#testLambertConicConformal2SP()
689     */
690    @Test
691    public void testEPSG_32040() throws FactoryException, TransformException {
692        toLinearUnit = PseudoEpsgFactory.R_US_FEET;
693        runProjectionTest(32040);
694    }
695
696    /**
697     * Tests the EPSG:31300 (<cite>Belge 1972 / Belge Lambert 72</cite>) projected CRS.
698     *
699     * <table class="ogc">
700     * <caption>CRS characteristics</caption>
701     * <tr><td>Projection method:</td> <td>Lambert Conic Conformal (2SP Belgium)</td></tr>
702     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
703     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
704     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in metres</td></tr>
705     * </table>
706     *
707     * @throws FactoryException if the math transform can not be created.
708     * @throws TransformException if the example point can not be transformed.
709     *
710     * @see ParameterizedTransformTest#testLambertConicConformalBelgium()
711     */
712    @Test
713    public void testEPSG_31300() throws FactoryException, TransformException {
714        runProjectionTest(31300);
715    }
716
717    /**
718     * Tests the EPSG:3035 (<cite>ETRS89 / LAEA Europe</cite>) projected CRS.
719     *
720     * <table class="ogc">
721     * <caption>CRS characteristics</caption>
722     * <tr><td>Projection method:</td> <td>Lambert Azimuthal Equal Area</td></tr>
723     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
724     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
725     * <tr><td>Output ordinates:</td>  <td>(<var>y</var>,<var>x</var>) in metres - <strong>note the axis order!</strong></td></tr>
726     * </table>
727     *
728     * @throws FactoryException if the math transform can not be created.
729     * @throws TransformException if the example point can not be transformed.
730     *
731     * @see ParameterizedTransformTest#testLambertAzimuthalEqualArea()
732     */
733    @Test
734    public void testEPSG_3035() throws FactoryException, TransformException {
735        swapxy = true;
736        runProjectionTest(3035);
737    }
738
739    /**
740     * Tests the EPSG:5041 (<cite>WGS 84 / UPS North (E,N)</cite>) projected CRS.
741     *
742     * <table class="ogc">
743     * <caption>CRS characteristics</caption>
744     * <tr><td>Projection method:</td> <td>Polar Stereographic (variant A)</td></tr>
745     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
746     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
747     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in metres</td></tr>
748     * </table>
749     *
750     * @throws FactoryException if the math transform can not be created.
751     * @throws TransformException if the example point can not be transformed.
752     *
753     * @see ParameterizedTransformTest#testPolarStereographicA()
754     */
755    @Test
756    public void testEPSG_5041() throws FactoryException, TransformException {
757        isPolar = true;
758        runProjectionTest(5041);
759    }
760
761    /**
762     * Tests the EPSG:32661 (<cite>WGS 84 / UPS North (N,E)</cite>) projected CRS.
763     * This CRS is identical to EPSG:5041 except for axis order.
764     *
765     * <table class="ogc">
766     * <caption>CRS characteristics</caption>
767     * <tr><td>Projection method:</td> <td>Polar Stereographic (variant A)</td></tr>
768     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
769     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
770     * <tr><td>Output ordinates:</td>  <td>(<var>y</var>,<var>x</var>) in metres - <strong>note the axis order!</strong></td></tr>
771     * </table>
772     *
773     * @throws FactoryException if the math transform can not be created.
774     * @throws TransformException if the example point can not be transformed.
775     *
776     * @see ParameterizedTransformTest#testPolarStereographicA()
777     */
778    @Test
779    public void testEPSG_32661() throws FactoryException, TransformException {
780        isPolar = true;
781        swapxy  = true;
782        runProjectionTest(32661);
783    }
784
785    /**
786     * Tests the EPSG:3032 (<cite>WGS 84 / Australian Antarctic Polar Stereographic</cite>) projected CRS.
787     *
788     * <table class="ogc">
789     * <caption>CRS characteristics</caption>
790     * <tr><td>Projection method:</td> <td>Polar Stereographic (variant B)</td></tr>
791     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
792     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
793     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in metres</td></tr>
794     * </table>
795     *
796     * @throws FactoryException if the math transform can not be created.
797     * @throws TransformException if the example point can not be transformed.
798     *
799     * @see ParameterizedTransformTest#testPolarStereographicB()
800     */
801    @Test
802    public void testEPSG_3032() throws FactoryException, TransformException {
803        isPolar = true;
804        runProjectionTest(3032);
805    }
806
807    /**
808     * Tests the EPSG:28992 (<cite>Amersfoort / RD New</cite>) projected CRS.
809     *
810     * <table class="ogc">
811     * <caption>CRS characteristics</caption>
812     * <tr><td>Projection method:</td> <td>Oblique Stereographic</td></tr>
813     * <tr><td>Prime meridian:</td>    <td>Greenwich</td></tr>
814     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
815     * <tr><td>Output ordinates:</td>  <td>(<var>x</var>,<var>y</var>) in metres</td></tr>
816     * </table>
817     *
818     * @throws FactoryException if the math transform can not be created.
819     * @throws TransformException if the example point can not be transformed.
820     *
821     * @see ParameterizedTransformTest#testObliqueStereographic()
822     */
823    @Test
824    public void testEPSG_28992() throws FactoryException, TransformException {
825        runProjectionTest(28992);
826    }
827
828    /**
829     * Tests the EPSG:2065 (<cite>CRS S-JTSK (Ferro) / Krovak</cite>) projected CRS.
830     *
831     * <table class="ogc">
832     * <caption>CRS characteristics</caption>
833     * <tr><td>Projection method:</td> <td>Krovak</td></tr>
834     * <tr><td>Prime meridian:</td>    <td>Ferro <strong>(17°40'W from Greenwich)</strong></td></tr>
835     * <tr><td>Source ordinates:</td>  <td>(φ,λ) in degrees</td></tr>
836     * <tr><td>Output ordinates:</td>  <td>(<var>y</var>,<var>x</var>) in metres, <strong>south oriented (S,W)</strong></td></tr>
837     * </table>
838     *
839     * @throws FactoryException if the math transform can not be created.
840     * @throws TransformException if the example point can not be transformed.
841     *
842     * @see ParameterizedTransformTest#testKrovak()
843     */
844    @Test
845    public void testEPSG_2065() throws FactoryException, TransformException {
846        swapxy = true;
847        flipxy = true;
848        primeMeridian = -(17 + 40.0/60);
849        runProjectionTest(2065);
850    }
851}