001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    Copyright © 2011-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.Set;
021import java.util.Map;
022import java.util.HashMap;
023import java.util.Collections;
024import java.util.Objects;
025import javax.measure.Unit;
026import javax.measure.quantity.Angle;
027import javax.measure.quantity.Length;
028
029import org.opengis.parameter.*;
030import org.opengis.referencing.*;
031import org.opengis.referencing.cs.*;
032import org.opengis.referencing.crs.*;
033import org.opengis.referencing.datum.*;
034import org.opengis.referencing.operation.*;
035import org.opengis.metadata.citation.Citation;
036import org.opengis.util.FactoryException;
037import org.opengis.test.util.PseudoFactory;
038import org.opengis.test.ValidatorContainer;
039import org.opengis.test.Units;
040
041import static org.junit.jupiter.api.Assertions.*;
042import static org.junit.jupiter.api.Assumptions.assumeTrue;
043import static org.opengis.test.referencing.ObjectFactoryTest.*;
044
045
046/**
047 * Creates referencing objects for a limited set of hard-coded EPSG codes
048 * using {@link ObjectFactory} and {@link MathTransformFactory}.
049 * This pseudo-factory can be used with implementations that do not support
050 * (or don't want to test) a "real" {@link CRSAuthorityFactory} for the EPSG database.
051 *
052 * @author  Martin Desruisseaux (Geomatys)
053 * @author  Johann Sorel (Geomatys)
054 * @author  Michael Arneson (INT)
055 * @version 3.1
056 * @since   3.1
057 */
058@SuppressWarnings("strictfp")   // Because we still target Java 11.
059public strictfp class PseudoEpsgFactory extends PseudoFactory implements DatumAuthorityFactory,
060        CSAuthorityFactory, CRSAuthorityFactory
061{
062    /**
063     * The reciprocal of the conversion from US feets to metres.
064     */
065    static final double R_US_FEET = 3.2808333333333333333;
066
067    /**
068     * Conversion from Clarke's 1865 feet to metres.
069     */
070    static final double CLARKE_FEET = 0.3047972654;
071
072    /**
073     * Conversion from feet to metres.
074     */
075    static final double FEET = 0.3048;
076
077    /**
078     * Conversion from links to metres
079     */
080    static final double LINKS = 0.66 * FEET;
081
082    /**
083     * Provider of predefined {@link Unit} instances (degree, metre, second, <i>etc</i>).
084     */
085    protected final Units units;
086
087    /**
088     * Factory to use for building {@link Datum} instances, or {@code null} if none.
089     */
090    protected final DatumFactory datumFactory;
091
092    /**
093     * Factory to use for building {@link CoordinateSystem} instances, or {@code null} if none.
094     */
095    protected final CSFactory csFactory;
096
097    /**
098     * Factory to use for building {@link CoordinateReferenceSystem} instances, or {@code null} if none.
099     */
100    protected final CRSFactory crsFactory;
101
102    /**
103     * Factory to use for building {@link Conversion} instances, or {@code null} if none.
104     */
105    protected final CoordinateOperationFactory copFactory;
106
107    /**
108     * Factory to use for building {@link MathTransform} instances, or {@code null} if none.
109     */
110    protected final MathTransformFactory mtFactory;
111
112    /**
113     * The set of validators to use for verifying objects conformance (never {@code null}).
114     */
115    protected final ValidatorContainer validators;
116
117    /**
118     * Creates a new pseudo-factory which will use the given factories.
119     *
120     * @param  units         provider of predefined {@link Unit} instances.
121     * @param  datumFactory  factory for creating {@link Datum} instances.
122     * @param  csFactory     factory for creating {@link CoordinateSystem} instances.
123     * @param  crsFactory    factory for creating {@link CoordinateReferenceSystem} instances.
124     * @param  copFactory    factory for creating {@link Conversion} instances.
125     * @param  mtFactory     factory for creating {@link MathTransform} instances.
126     * @param  validators    the set of validators to use for verifying objects conformance,
127     *                       Cannot be {@code null}; if there is no particular validators,
128     *                       use {@link org.opengis.test.Validators#DEFAULT}.
129     */
130    public PseudoEpsgFactory(
131            final Units                           units,
132            final DatumFactory             datumFactory,
133            final CSFactory                   csFactory,
134            final CRSFactory                 crsFactory,
135            final CoordinateOperationFactory copFactory,
136            final MathTransformFactory        mtFactory,
137            final ValidatorContainer         validators)
138    {
139        this.units        = Objects.requireNonNull(units, "The units cannot be null. Do you mean Units.getDefault()?");
140        this.datumFactory = datumFactory;
141        this.csFactory    = csFactory;
142        this.crsFactory   = crsFactory;
143        this.copFactory   = copFactory;
144        this.mtFactory    = mtFactory;
145        this.validators   = Objects.requireNonNull(validators, "The validators cannot be null. Do you mean Validators.DEFAULT?");
146    }
147
148    /**
149     * Returns the given EPSG code as an integer.
150     *
151     * @param  code  the EPSG code to parse.
152     * @return the EPSG code as an integer.
153     * @throws NoSuchAuthorityCodeException if the given code cannot be parsed as an integer.
154     */
155    private static int parseCode(String code) throws NoSuchAuthorityCodeException {
156        final int s = code.lastIndexOf(':');
157        if (s >= 0) {
158            final String authority = code.substring(0, s).trim();
159            if (!authority.equalsIgnoreCase("EPSG")) {
160                throw new NoSuchAuthorityCodeException("Unsupported \"" + authority + "\" authority.", "EPSG", code);
161            }
162            code = code.substring(s+1).trim();
163        }
164        try {
165            return Integer.parseInt(code);
166        } catch (NumberFormatException cause) {
167            NoSuchAuthorityCodeException e = new NoSuchAuthorityCodeException(
168                    "Unparseable EPSG code: " + code, "EPSG", code);
169            e.initCause(cause);
170            throw e;
171        }
172    }
173
174    /**
175     * Creates the exception to be thrown when the given code has not been recognized.
176     *
177     * @param  code   the code as a numerical value.
178     * @param  given  the code as given by the user.
179     * @return the exception to throw.
180     */
181    private static NoSuchAuthorityCodeException noSuchAuthorityCode(final int code, final String given) {
182        final String codeAsText = String.valueOf(code);
183        return new NoSuchAuthorityCodeException("No case implemented for EPSG:" + codeAsText,
184                "EPSG", codeAsText, given);
185    }
186
187    /**
188     * Returns the organization or party responsible for definition and maintenance of the database.
189     * The default implementation returns {@code null}.
190     *
191     * @return the organization responsible for definition of the database.
192     */
193    @Override
194    public Citation getAuthority() {
195        return null;
196    }
197
198    /**
199     * Returns the set of authority codes for objects of the given type.
200     * The default implementation returns an empty set.
201     *
202     * @param  type  the spatial reference objects type.
203     * @return the set of authority codes for spatial reference objects of the given type.
204     * @throws FactoryException if this method cannot provide the requested information.
205     *
206     * @todo Needs to be implemented.
207     */
208    @Override
209    public Set<String> getAuthorityCodes(final Class<? extends IdentifiedObject> type) throws FactoryException {
210        return Collections.emptySet();
211    }
212
213    /**
214     * Builds a map of properties for a referencing object to be built. The map shall contain
215     * at least the {@link IdentifiedObject#NAME_KEY} identifier associated to the given value.
216     * Subclasses can override this method in order to provide more information if they wish.
217     *
218     * @param  code  The EPSG code of the object being built.
219     * @param  name  The name of the object being built.
220     * @return a map containing the properties for the object to create.
221     */
222    protected Map<String,Object> createPropertiesMap(final int code, final String name) {
223        final Map<String,Object> properties = new HashMap<>(4);
224        assertNull(properties.put(IdentifiedObject.NAME_KEY, name));
225        assertNull(properties.put(IdentifiedObject.IDENTIFIERS_KEY, new EPSGIdentifier(code)));
226        return properties;
227    }
228
229    /**
230     * Returns an arbitrary object from a code.
231     *
232     * <table class="ogc">
233     *   <caption>Supported codes</caption>
234     *   <tr><th>Code</th> <th>Name</th></tr>
235     *   <tr><td>4326</td> <td>WGS 84</td></tr>
236     *   <tr><td>6326</td> <td>World Geodetic System 1984</td></tr>
237     *   <tr><td>6422</td> <td>Ellipsoidal 2D CS. Axes: latitude, longitude. Orientations: north, east. UoM: degree</td></tr>
238     * </table>
239     *
240     * @param  code  value allocated by authority.
241     * @return the datum for the given code.
242     * @throws FactoryException if the object creation failed.
243     *
244     * @deprecated This method is ambiguous.
245     */
246    @Override
247    @SuppressWarnings("removal")
248    @Deprecated(since = "3.1", forRemoval = true)
249    public IdentifiedObject createObject(final String code) throws FactoryException {
250        final int id = parseCode(code);
251        switch (id) {
252            case 6326: return createDatum(code);
253            case 6422: return createCoordinateSystem(code);
254            case 4326: return createCoordinateReferenceSystem(code);
255            default:   throw noSuchAuthorityCode(id, code);
256        }
257    }
258
259
260
261
262    ///////////////////////////////////////////////////////////////////////////////////////////////
263    ///////////////////////////////                                 ///////////////////////////////
264    ///////////////////////////////    D A T U M   F A C T O R Y    ///////////////////////////////
265    ///////////////////////////////                                 ///////////////////////////////
266    ///////////////////////////////////////////////////////////////////////////////////////////////
267
268    /**
269     * Returns an arbitrary {@linkplain Datum datum} from a code.
270     *
271     * <table class="ogc">
272     *   <caption>Supported codes</caption>
273     *   <tr><th>Code</th> <th>Name</th></tr>
274     *   <tr><td>6326</td> <td>World Geodetic System 1984</td></tr>
275     * </table>
276     *
277     * @param  code  value allocated by authority.
278     * @return the datum for the given code.
279     * @throws FactoryException if the object creation failed.
280     */
281    @Override
282    public Datum createDatum(final String code) throws FactoryException {
283        final int id = parseCode(code);
284        switch (id) {
285            case 6326: return createGeodeticDatum(code);
286            default:   throw noSuchAuthorityCode(id, code);
287        }
288    }
289
290    /**
291     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
292     *
293     * @throws FactoryException if this method cannot provide the requested information.
294     */
295    @Override
296    public EngineeringDatum createEngineeringDatum(String code) throws FactoryException {
297        final int id = parseCode(code);
298        switch (id) {
299            default:   throw noSuchAuthorityCode(id, code);
300        }
301    }
302
303    /**
304     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
305     *
306     * @throws FactoryException if this method cannot provide the requested information.
307     *
308     * @deprecated {@code ImageDatum} is replaced by {@link EngineeringDatum} as of ISO 19111:2019.
309     */
310    @Override
311    @Deprecated(since="3.1")
312    public ImageDatum createImageDatum(String code) throws FactoryException {
313        final int id = parseCode(code);
314        switch (id) {
315            default:   throw noSuchAuthorityCode(id, code);
316        }
317    }
318
319    /**
320     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
321     *
322     * @throws FactoryException if this method cannot provide the requested information.
323     */
324    @Override
325    public VerticalDatum createVerticalDatum(String code) throws FactoryException {
326        final int id = parseCode(code);
327        switch (id) {
328            default:   throw noSuchAuthorityCode(id, code);
329        }
330    }
331
332    /**
333     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
334     *
335     * @throws FactoryException if this method cannot provide the requested information.
336     */
337    @Override
338    public TemporalDatum createTemporalDatum(String code) throws FactoryException {
339        final int id = parseCode(code);
340        switch (id) {
341            default:   throw noSuchAuthorityCode(id, code);
342        }
343    }
344
345    /**
346     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
347     *
348     * @throws FactoryException if this method cannot provide the requested information.
349     */
350    @Override
351    public ParametricDatum createParametricDatum(String code) throws FactoryException {
352        final int id = parseCode(code);
353        switch (id) {
354            default:   throw noSuchAuthorityCode(id, code);
355        }
356    }
357
358    /**
359     * Returns a {@linkplain GeodeticDatum geodetic reference frame} from a code.
360     *
361     * <table class="ogc">
362     *   <caption>Supported codes</caption>
363     *   <tr><th>Code</th> <th>Name</th></tr>
364     *   <tr><td>6326</td> <td>World Geodetic System 1984</td></tr>
365     *   <tr><td>6284</td> <td>Pulkovo 1942</td></tr>
366     * </table>
367     *
368     * @param  code  value allocated by authority.
369     * @return the datum for the given code.
370     * @throws FactoryException if the object creation failed.
371     */
372    @Override
373    public GeodeticDatum createGeodeticDatum(final String code) throws FactoryException {
374        final String name;
375        final int ellipsoid;
376        final int primeMeridian;
377        final int id = parseCode(code);
378        switch (id) {
379            case 6326: name="World Geodetic System 1984"; ellipsoid=7030; primeMeridian=8901; break;
380            case 6284: name="Pulkovo 1942";               ellipsoid=7024; primeMeridian=8901; break;
381            default:   throw noSuchAuthorityCode(id, code);
382        }
383        assumeTrue(datumFactory != null, NO_DATUM_FACTORY);
384        final GeodeticDatum object = datumFactory.createGeodeticDatum(createPropertiesMap(id, name),
385                createEllipsoid    (String.valueOf(ellipsoid)),
386                createPrimeMeridian(String.valueOf(primeMeridian)));
387        validators.validate(object);
388        return object;
389    }
390
391    /**
392     * Returns an {@linkplain Ellipsoid ellipsoid} from a code.
393     *
394     * <table class="ogc">
395     *   <caption>Supported codes</caption>
396     *   <tr><th>Code</th> <th>Name</th></tr>
397     *   <tr><td>7001</td> <td>Airy 1830</td></tr>
398     *   <tr><td>7004</td> <td>Bessel 1841</td></tr>
399     *   <tr><td>7011</td> <td>Clarke 1880 (IGN)</td></tr>
400     *   <tr><td>7019</td> <td>GRS 1980</td></tr>
401     *   <tr><td>7022</td> <td>International 1924</td></tr>
402     *   <tr><td>7024</td> <td>Krassowsky 1940</td></tr>
403     *   <tr><td>7030</td> <td>WGS 84</td></tr>
404     * </table>
405     *
406     * @param  code  value allocated by authority.
407     * @return the ellipsoid for the given code.
408     * @throws FactoryException if the object creation failed.
409     */
410    @Override
411    public Ellipsoid createEllipsoid(final String code) throws FactoryException {
412        final String name;
413        final double semiMajorAxis;
414        double semiMinorAxis = Double.NaN;
415        double inverseFlattening = Double.NaN;
416        int    unitCode = 9001;                     // Default unit is metre.
417        final int id = parseCode(code);
418        switch (id) {
419            case 7030: name="WGS 84";             semiMajorAxis=6378137;     inverseFlattening=298.257223563; break;
420            case 7019: name="GRS 1980";           semiMajorAxis=6378137;     inverseFlattening=298.2572221;   break;
421            case 7001: name="Airy 1830";          semiMajorAxis=6377563.396; inverseFlattening=299.3249646;   break;
422            case 7004: name="Bessel 1841";        semiMajorAxis=6377397.155; inverseFlattening=299.1528128;   break;
423            case 7024: name="Krassowsky 1940";    semiMajorAxis=6378245;     inverseFlattening=298.3;         break;
424            case 7022: name="International 1924"; semiMajorAxis=6378388;     inverseFlattening=297;           break;
425            case 7011: name="Clarke 1880 (IGN)";  semiMajorAxis=6378249.2;   semiMinorAxis=6356515;           break;
426            default:   throw noSuchAuthorityCode(id, code);
427        }
428        assumeTrue(datumFactory != null, NO_DATUM_FACTORY);
429        final Map<String,?> properties = createPropertiesMap(id, name);
430        final Unit<Length> unit = createUnit(String.valueOf(unitCode)).asType(Length.class);
431        final Ellipsoid object;
432        if (Double.isNaN(inverseFlattening)) {
433            object = datumFactory.createEllipsoid(properties, semiMajorAxis, semiMinorAxis, unit);
434        } else {
435            object = datumFactory.createFlattenedSphere(properties, semiMajorAxis, inverseFlattening, unit);
436        }
437        validators.validate(object);
438        return object;
439    }
440
441    /**
442     * Returns a {@linkplain PrimeMeridian prime meridian} from a EPSG code.
443     *
444     * <table class="ogc">
445     *   <caption>Supported codes</caption>
446     *   <tr><th>Code</th> <th>Name</th></tr>
447     *   <tr><td>8901</td> <td>Greenwich</td></tr>
448     *   <tr><td>8903</td> <td>Paris</td></tr>
449     *   <tr><td>8908</td> <td>Jakarta</td></tr>
450     * </table>
451     *
452     * @param  code  value allocated by authority.
453     * @return the prime meridian for the given code.
454     * @throws FactoryException if the object creation failed.
455     */
456    @Override
457    public PrimeMeridian createPrimeMeridian(final String code) throws FactoryException {
458        final String name;
459        final double longitude;
460        final int    unit;
461        final int id = parseCode(code);
462        switch (id) {
463            case 8901: name="Greenwich"; longitude=  0.0;              unit=9102; break;
464            case 8903: name="Paris";     longitude=  2.5969213;        unit=9105; break;
465            case 8908: name="Jakarta";   longitude=106.80771944444444; unit=9102; break;
466            default:   throw noSuchAuthorityCode(id, code);
467        }
468        assumeTrue(datumFactory != null, NO_DATUM_FACTORY);
469        final PrimeMeridian object = datumFactory.createPrimeMeridian(createPropertiesMap(id, name),
470                longitude, createUnit(String.valueOf(unit)).asType(Angle.class));
471        validators.validate(object);
472        return object;
473    }
474
475
476
477
478    ///////////////////////////////////////////////////////////////////////////////////////////////
479    ///////////////////                                                         ///////////////////
480    ///////////////////    C O O R D I N A T E   S Y S T E M   F A C T O R Y    ///////////////////
481    ///////////////////                                                         ///////////////////
482    ///////////////////////////////////////////////////////////////////////////////////////////////
483
484    /**
485     * Returns an arbitrary {@linkplain CoordinateSystem coordinate system} from a code.
486     *
487     * <table class="ogc">
488     *   <caption>Supported codes</caption>
489     *   <tr><th>Code</th> <th>Name</th></tr>
490     *   <tr><td>6422</td> <td>Ellipsoidal 2D CS. Axes: latitude, longitude. Orientations: north, east. UoM: degree</td></tr>
491     * </table>
492     *
493     * @param  code  value allocated by authority.
494     * @return the coordinate system for the given code.
495     * @throws FactoryException if the object creation failed.
496     */
497    @Override
498    public CoordinateSystem createCoordinateSystem(final String code) throws FactoryException {
499        final int id = parseCode(code);
500        switch (id) {
501            case 6422: return createEllipsoidalCS(code);
502            default:   throw noSuchAuthorityCode(id, code);
503        }
504    }
505
506    /**
507     * Creates a Cartesian coordinate system from a code.
508     *
509     * <table class="ogc">
510     *   <caption>Supported codes</caption>
511     *   <tr><th>Code</th> <th>Name</th></tr>
512     *   <tr><td>4400</td> <td>Cartesian 2D CS. Axes: easting, northing (E,N). Orientations: east, north. UoM: m.</td></tr>
513     *   <tr><td>4495</td> <td>Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: ft.</td></tr>
514     *   <tr><td>4497</td> <td>Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: ftUS.</td></tr>
515     *   <tr><td>4498</td> <td>Cartesian 2D CS. Axes: easting, northing (Y,X). Orientations: east, north. UoM: m.</td></tr>
516     *   <tr><td>4499</td> <td>Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: m.</td></tr>
517     *   <tr><td>4500</td> <td>Cartesian 2D CS. Axes: northing, easting (N,E). Orientations: north, east. UoM: m.</td></tr>
518     *   <tr><td>4530</td> <td>Cartesian 2D CS. Axes: northing, easting (X,Y). Orientations: north, east. UoM: m.</td></tr>
519     *   <tr><td>4532</td> <td>Cartesian 2D CS. Axes: northing, easting (Y,X). Orientations: north, east. UoM: m.</td></tr>
520     *   <tr><td>4534</td> <td>Cartesian 2D CS. Axes: northing, easting (no abbrev). Orientations: north, east. UoM: m.</td></tr>
521     *   <tr><td>6503</td> <td>Cartesian 2D CS. Axes: westing, southing (Y,X). Orientations: west, south. UoM: m.</td></tr>
522     *   <tr><td>6500</td> <td>Earth centred, earth fixed, righthanded 3D coordinate system,
523     *     consisting of 3 orthogonal axes with X and Y axes in the equatorial plane,
524     *     positive Z-axis parallel to mean earth rotation axis and pointing towards North Pole.
525     *     UoM: m</td></tr>
526     * </table>
527     *
528     * @param  code  value allocated by authority.
529     * @return the coordinate system for the given code.
530     * @throws FactoryException if the object creation failed.
531     */
532    @Override
533    public CartesianCS createCartesianCS(final String code) throws FactoryException {
534        final String name;
535        final int[] axes;
536        final int id = parseCode(code);
537        switch (id) {
538            case 4400: name = "Cartesian 2D CS. Axes: easting, northing (E,N). Orientations: east, north. UoM: m.";       axes = new int[] {  1,   2}; break;
539            case 4495: name = "Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: ft.";      axes = new int[] { 33,  34}; break;
540            case 4497: name = "Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: ftUS.";    axes = new int[] { 37,  38}; break;
541            case 4498: name = "Cartesian 2D CS. Axes: easting, northing (Y,X). Orientations: east, north. UoM: m.";       axes = new int[] { 39,  40}; break;
542            case 4499: name = "Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: m.";       axes = new int[] { 41,  42}; break;
543            case 4500: name = "Cartesian 2D CS. Axes: northing, easting (N,E). Orientations: north, east. UoM: m.";       axes = new int[] { 44,  43}; break;
544            case 4530: name = "Cartesian 2D CS. Axes: northing, easting (X,Y). Orientations: north, east. UoM: m.";       axes = new int[] { 48,  47}; break;
545            case 4532: name = "Cartesian 2D CS. Axes: northing, easting (Y,X). Orientations: north, east. UoM: m.";       axes = new int[] { 52,  51}; break;
546            case 4534: name = "Cartesian 2D CS. Axes: northing, easting (no abbrev). Orientations: north, east. UoM: m."; axes = new int[] {183, 184}; break;
547            case 6503: name = "Cartesian 2D CS. Axes: westing, southing (Y,X). Orientations: west, south. UoM: m.";       axes = new int[] {122, 123}; break;
548            case 6500: {
549                name = "Earth centred, earth fixed, righthanded 3D coordinate system, "
550                     + "consisting of 3 orthogonal axes with X and Y axes in the equatorial plane, "
551                     + "positive Z-axis parallel to mean earth rotation axis and pointing towards North Pole. "
552                     + "UoM: m";
553                axes = new int[] {115, 116, 117};
554                break;
555            }
556            default: throw noSuchAuthorityCode(id, code);
557        }
558        assumeTrue(csFactory != null, NO_CS_FACTORY);
559        final Map<String,?> properties = createPropertiesMap(id, name);
560        final CartesianCS object;
561        if (axes.length >= 3) {
562            object = csFactory.createCartesianCS(properties,
563                    createCoordinateSystemAxis(String.valueOf(axes[0])),
564                    createCoordinateSystemAxis(String.valueOf(axes[1])),
565                    createCoordinateSystemAxis(String.valueOf(axes[2])));
566        } else {
567            object = csFactory.createCartesianCS(properties,
568                    createCoordinateSystemAxis(String.valueOf(axes[0])),
569                    createCoordinateSystemAxis(String.valueOf(axes[1])));
570        }
571        validators.validate(object);
572        return object;
573    }
574
575    /**
576     * Creates a polar coordinate system from a code.
577     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
578     *
579     * @param  code  value allocated by authority.
580     * @return the coordinate system for the given code.
581     * @throws FactoryException if this method cannot provide the requested information.
582     */
583    @Override
584    public PolarCS createPolarCS(final String code) throws FactoryException {
585        final int id = parseCode(code);
586        switch (id) {
587            default: throw noSuchAuthorityCode(id, code);
588        }
589    }
590
591    /**
592     * Creates a cylindrical coordinate system from a code.
593     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
594     *
595     * @param  code  value allocated by authority.
596     * @return the coordinate system for the given code.
597     * @throws FactoryException if this method cannot provide the requested information.
598     */
599    @Override
600    public CylindricalCS createCylindricalCS(final String code) throws FactoryException {
601        final int id = parseCode(code);
602        switch (id) {
603            default: throw noSuchAuthorityCode(id, code);
604        }
605    }
606
607    /**
608     * Creates a spherocal coordinate system from a code.
609     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
610     *
611     * @param  code  value allocated by authority.
612     * @return the coordinate system for the given code.
613     * @throws FactoryException if this method cannot provide the requested information.
614     */
615    @Override
616    public SphericalCS createSphericalCS(final String code) throws FactoryException {
617        final int id = parseCode(code);
618        switch (id) {
619            default: throw noSuchAuthorityCode(id, code);
620        }
621    }
622
623    /**
624     * Creates an ellipsoidal coordinate system from a code.
625     *
626     * <table class="ogc">
627     *   <caption>Supported codes</caption>
628     *   <tr><th>Code</th> <th>Name</th></tr>
629     *   <tr><td>6403</td> <td>Ellipsoidal 2D CS. Axes: latitude, longitude. Orientations: north, east. UoM: grads.</td></tr>
630     *   <tr><td>6422</td> <td>Ellipsoidal 2D CS. Axes: latitude, longitude. Orientations: north, east. UoM: degree</td></tr>
631     *   <tr><td>6423</td> <td>Ellipsoidal 3D CS. Axes: latitude, longitude, ellipsoidal height. Orientations: north, east, up. UoM: degree, degree, metre.</td></tr>
632     *   <tr><td>6424</td> <td>Ellipsoidal 2D CS. Axes: longitude, latitude. Orientations: east, north. UoM: degree</td></tr>
633     * </table>
634     *
635     * @param  code  value allocated by authority.
636     * @return the coordinate system for the given code.
637     * @throws FactoryException if the object creation failed.
638     */
639    @Override
640    public EllipsoidalCS createEllipsoidalCS(final String code) throws FactoryException {
641        final String name;
642        final int[] axes;
643        final int id = parseCode(code);
644        switch (id) {
645            case 6403: {
646                name = "Ellipsoidal 2D CS. Axes: latitude, longitude. "
647                     + "Orientations: north, east. "
648                     + "UoM: grads. ";
649                axes = new int[] {58, 59};              // Geodetic latitude, Geodetic longitude
650                break;
651            }
652            case 6422: {
653                name = "Ellipsoidal 2D CS. Axes: latitude, longitude. "
654                     + "Orientations: north, east. "
655                     + "UoM: degree";
656                axes = new int[] {106, 107};            // Geodetic latitude, Geodetic longitude
657                break;
658            }
659            case 6423: {
660                name = "Ellipsoidal 3D CS. Axes: latitude, longitude, ellipsoidal height. "
661                     + "Orientations: north, east, up. "
662                     + "UoM: degree, degree, metre.";
663                axes = new int[] {108, 109, 110};       // Geodetic latitude, Geodetic longitude, Ellipsoidal height
664                break;
665            }
666            case 6424: {
667                name = "Ellipsoidal 2D CS. Axes: longitude, latitude. "
668                     + "Orientations: east, north. "
669                     + "UoM: degree";
670                axes = new int[] {220, 221};            // Geodetic longitude, Geodetic latitude
671                break;
672            }
673            default: throw noSuchAuthorityCode(id, code);
674        }
675        assumeTrue(csFactory != null, NO_CS_FACTORY);
676        final Map<String,?> properties = createPropertiesMap(id, name);
677        final EllipsoidalCS object;
678        if (axes.length >= 3) {
679            object = csFactory.createEllipsoidalCS(properties,
680                    createCoordinateSystemAxis(String.valueOf(axes[0])),
681                    createCoordinateSystemAxis(String.valueOf(axes[1])),
682                    createCoordinateSystemAxis(String.valueOf(axes[2])));
683        } else {
684            object = csFactory.createEllipsoidalCS(properties,
685                    createCoordinateSystemAxis(String.valueOf(axes[0])),
686                    createCoordinateSystemAxis(String.valueOf(axes[1])));
687        }
688        validators.validate(object);
689        return object;
690    }
691
692    /**
693     * Creates a vertical coordinate system from a code.
694     *
695     * <table class="ogc">
696     *   <caption>Supported codes</caption>
697     *   <tr><th>Code</th> <th>Name</th></tr>
698     *   <tr><td>1030</td> <td>Vertical CS. Axis: height (H). Orientation: up. UoM: ft.</td></tr>
699     *   <tr><td>6495</td> <td>Vertical CS. Axis: depth (D). Orientation: down. UoM: ft.</td></tr>
700     *   <tr><td>6497</td> <td>Vertical CS. Axis: height (H). Orientation: up. UoM: ftUS.</td></tr>
701     *   <tr><td>6498</td> <td>Vertical CS. Axis: depth (D). Orientation: down. UoM: m.;</td></tr>
702     *   <tr><td>6499</td> <td>Vertical CS. Axis: height (H). Orientation: up. UoM: m.</td></tr>
703     * </table>
704     *
705     * @param  code  value allocated by authority.
706     * @return the coordinate system for the given code.
707     * @throws FactoryException if this method cannot provide the requested information.
708     */
709    @Override
710    public VerticalCS createVerticalCS(final String code) throws FactoryException {
711        final int id = parseCode(code);
712        final String name;
713        final int axis;
714        switch (id) {
715            case 1030: name = "Vertical CS. Axis: height (H). Orientation: up. UoM: ft.";   axis = 1082; break;
716            case 6495: name = "Vertical CS. Axis: depth (D). Orientation: down. UoM: ft.";  axis =  214; break;
717            case 6497: name = "Vertical CS. Axis: height (H). Orientation: up. UoM: ftUS."; axis =  112; break;
718            case 6498: name = "Vertical CS. Axis: depth (D). Orientation: down. UoM: m.";   axis =  113; break;
719            case 6499: name = "Vertical CS. Axis: height (H). Orientation: up. UoM: m.";    axis =  114; break;
720            default: throw noSuchAuthorityCode(id, code);
721        }
722        assumeTrue(csFactory != null, NO_CS_FACTORY);
723        final Map<String,?> properties = createPropertiesMap(id, name);
724        final VerticalCS object = csFactory.createVerticalCS(properties,
725                createCoordinateSystemAxis(String.valueOf(axis)));
726        validators.validate(object);
727        return object;
728    }
729
730    /**
731     * Creates a temporal coordinate system from a code.
732     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
733     *
734     * @param  code  value allocated by authority.
735     * @return the coordinate system for the given code.
736     * @throws FactoryException if this method cannot provide the requested information.
737     */
738    @Override
739    public TimeCS createTimeCS(final String code) throws FactoryException {
740        final int id = parseCode(code);
741        switch (id) {
742            default:   throw noSuchAuthorityCode(id, code);
743        }
744    }
745
746    /**
747     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
748     *
749     * @throws FactoryException if this method cannot provide the requested information.
750     */
751    @Override
752    public ParametricCS createParametricCS(String code) throws FactoryException {
753        final int id = parseCode(code);
754        switch (id) {
755            default:   throw noSuchAuthorityCode(id, code);
756        }
757    }
758
759    /**
760     * Returns a {@linkplain CoordinateSystemAxis coordinate system axis} from a code.
761     *
762     * <table class="ogc">
763     *   <caption>Supported codes</caption>
764     *   <tr><th>Code</th>          <th>Name</th>        <th>Abbreviation</th>    <th>Unit</th></tr>
765     *   <tr><td>1, 43</td>         <td>Easting</td>                <td>E</td>    <td>metre</td></tr>
766     *   <tr><td>2, 44</td>         <td>Northing</td>               <td>N</td>    <td>metre</td></tr>
767     *   <tr><td>41, 51</td>        <td>Easting</td>                <td>X</td>    <td>metre</td></tr>
768     *   <tr><td>42, 52</td>        <td>Northing</td>               <td>Y</td>    <td>metre</td></tr>
769     *   <tr><td>39, 47</td>        <td>Easting</td>                <td>Y</td>    <td>metre</td></tr>
770     *   <tr><td>40, 48</td>        <td>Northing</td>               <td>X</td>    <td>metre</td></tr>
771     *   <tr><td>33</td>            <td>Easting</td>                <td>X</td>    <td>foot</td></tr>
772     *   <tr><td>34</td>            <td>Northing</td>               <td>Y</td>    <td>foot</td></tr>
773     *   <tr><td>37</td>            <td>Easting</td>                <td>X</td>    <td>foot US</td></tr>
774     *   <tr><td>38</td>            <td>Northing</td>               <td>Y</td>    <td>foot US</td></tr>
775     *   <tr><td>122</td>           <td>Westing</td>                <td>Y</td>    <td>metre</td></tr>
776     *   <tr><td>123</td>           <td>Southing</td>               <td>X</td>    <td>metre</td></tr>
777     *   <tr><td>183</td>           <td>Northing</td>               <td>none</td> <td>metre</td></tr>
778     *   <tr><td>184</td>           <td>Easting</td>                <td>none</td> <td>metre</td></tr>
779     *   <tr><td>58</td>            <td>Geodetic latitude</td>      <td>Lat</td>  <td>grad</td></tr>
780     *   <tr><td>59</td>            <td>Geodetic longitude</td>     <td>Long</td> <td>grad</td></tr>
781     *   <tr><td>106, 108, 221</td> <td>Geodetic latitude</td>      <td>Lat</td>  <td>degree</td></tr>
782     *   <tr><td>107, 109, 220</td> <td>Geodetic longitude</td>     <td>Long</td> <td>degree</td></tr>
783     *   <tr><td>110</td>           <td>Ellipsoidal height</td>     <td>h</td>    <td>metre</td></tr>
784     *   <tr><td>115</td>           <td>Geocentric X</td>           <td>X</td>    <td>metre</td></tr>
785     *   <tr><td>116</td>           <td>Geocentric Y</td>           <td>Y</td>    <td>metre</td></tr>
786     *   <tr><td>117</td>           <td>Geocentric Z</td>           <td>Z</td>    <td>metre</td></tr>
787     *   <tr><td>112</td>           <td>Gravity-related height</td> <td>H</td>    <td>foot US</td></tr>
788     *   <tr><td>113</td>           <td>Gravity-related depth</td>  <td>D</td>    <td>metre</td></tr>
789     *   <tr><td>114</td>           <td>Gravity-related height</td> <td>H</td>    <td>metre</td></tr>
790     *   <tr><td>214</td>           <td>Gravity-related depth</td>  <td>D</td>    <td>foot</td></tr>
791     *   <tr><td>1082</td>          <td>Gravity-related height</td> <td>H</td>    <td>foot</td></tr>
792     * </table>
793     *
794     * @param  code  value allocated by authority.
795     * @return the axis for the given code.
796     * @throws FactoryException if the object creation failed.
797     */
798    @Override
799    public CoordinateSystemAxis createCoordinateSystemAxis(final String code) throws FactoryException {
800        final String name;
801        final String abbreviation;
802        final AxisDirection direction;
803        final int unit;
804        final int id = parseCode(code);
805        switch (id) {
806            case    1: case 43: name="Easting";  abbreviation="E";    direction=AxisDirection.EAST;  unit=9001; break;
807            case    2: case 44: name="Northing"; abbreviation="N";    direction=AxisDirection.NORTH; unit=9001; break;
808            case   41: case 51: name="Easting";  abbreviation="X";    direction=AxisDirection.EAST;  unit=9001; break;
809            case   42: case 52: name="Northing"; abbreviation="Y";    direction=AxisDirection.NORTH; unit=9001; break;
810            case   39: case 47: name="Easting";  abbreviation="Y";    direction=AxisDirection.EAST;  unit=9001; break;
811            case   40: case 48: name="Northing"; abbreviation="X";    direction=AxisDirection.NORTH; unit=9001; break;
812            case   33:          name="Easting";  abbreviation="X";    direction=AxisDirection.EAST;  unit=9002; break;
813            case   34:          name="Northing"; abbreviation="Y";    direction=AxisDirection.NORTH; unit=9002; break;
814            case   37:          name="Easting";  abbreviation="X";    direction=AxisDirection.EAST;  unit=9003; break;
815            case   38:          name="Northing"; abbreviation="Y";    direction=AxisDirection.NORTH; unit=9003; break;
816            case  122:          name="Westing";  abbreviation="Y";    direction=AxisDirection.WEST;  unit=9001; break;
817            case  123:          name="Southing"; abbreviation="X";    direction=AxisDirection.SOUTH; unit=9001; break;
818            case  183:          name="Northing"; abbreviation="none"; direction=AxisDirection.NORTH; unit=9001; break;
819            case  184:          name="Easting";  abbreviation="none"; direction=AxisDirection.EAST;  unit=9001; break;
820            case  108: case 221:
821            case  106: name="Geodetic latitude";  abbreviation="Lat";  direction=AxisDirection.NORTH;        unit=9122; break;
822            case   58: name="Geodetic latitude";  abbreviation="Lat";  direction=AxisDirection.NORTH;        unit=9105; break;
823            case  109: case 220:
824            case  107: name="Geodetic longitude";     abbreviation="Long"; direction=AxisDirection.EAST;         unit=9122; break;
825            case   59: name="Geodetic longitude";     abbreviation="Long"; direction=AxisDirection.EAST;         unit=9105; break;
826            case  110: name="Ellipsoidal height";     abbreviation="h";    direction=AxisDirection.UP;           unit=9001; break;
827            case  115: name="Geocentric X";           abbreviation="X";    direction=AxisDirection.GEOCENTRIC_X; unit=9001; break;
828            case  116: name="Geocentric Y";           abbreviation="Y";    direction=AxisDirection.GEOCENTRIC_Y; unit=9001; break;
829            case  117: name="Geocentric Z";           abbreviation="Z";    direction=AxisDirection.GEOCENTRIC_Z; unit=9001; break;
830            case  112: name="Gravity-related height"; abbreviation="H";    direction=AxisDirection.UP;           unit=9003; break;
831            case  113: name="Gravity-related depth";  abbreviation="D";    direction=AxisDirection.DOWN;         unit=9001; break;
832            case  114: name="Gravity-related height"; abbreviation="H";    direction=AxisDirection.UP;           unit=9001; break;
833            case  214: name="Gravity-related depth";  abbreviation="D";    direction=AxisDirection.DOWN;         unit=9002; break;
834            case 1082: name="Gravity-related height"; abbreviation="H";    direction=AxisDirection.UP;           unit=9002; break;
835            default:  throw noSuchAuthorityCode(id, code);
836        }
837        assumeTrue(csFactory != null, NO_CS_FACTORY);
838        final CoordinateSystemAxis object = csFactory.createCoordinateSystemAxis(createPropertiesMap(id, name),
839                abbreviation, direction, createUnit(String.valueOf(unit)));
840        validators.validate(object);
841        return object;
842    }
843
844    /**
845     * Returns an {@linkplain Unit unit} from a code.
846     *
847     * <table class="ogc">
848     *   <caption>Supported codes</caption>
849     *   <tr><th>Code</th> <th>Name</th></tr>
850     *   <tr><td>9001</td> <td>metre</td></tr>
851     *   <tr><td>9002</td> <td>foot</td></tr>
852     *   <tr><td>9003</td> <td>foot US survey</td></tr>
853     *   <tr><td>9102</td> <td>degree</td></tr>
854     *   <tr><td>9105</td> <td>grad</td></tr>
855     *   <tr><td>9122</td> <td>degree (supplier to define representation)</td></tr>
856     * </table>
857     *
858     * @param  code  value allocated by authority.
859     * @return the unit for the given code.
860     * @throws FactoryException if the object creation failed.
861     */
862    @Override
863    public Unit<?> createUnit(final String code) throws FactoryException {
864        final int id = parseCode(code);
865        switch (id) {
866            case 9001: return units.metre();
867            case 9002: return units.foot();
868            case 9003: return units.footSurveyUS();
869            case 9122: // Fall through
870            case 9102: return units.degree();
871            case 9105: return units.grad();
872            default:   throw noSuchAuthorityCode(id, code);
873        }
874    }
875
876
877
878
879    ///////////////////////////////////////////////////////////////////////////////////////////////
880    /////////                                                                             /////////
881    /////////    C O O R D I N A T E   R E F E R E N C E   S Y S T E M   F A C T O R Y    /////////
882    /////////                                                                             /////////
883    ///////////////////////////////////////////////////////////////////////////////////////////////
884
885    /**
886     * Returns an arbitrary {@linkplain CoordinateReferenceSystem coordinate reference system} from a code.
887     *
888     * <table class="ogc">
889     *   <caption>Supported codes</caption>
890     *   <tr><th>Code</th> <th>Name</th></tr>
891     *   <tr><td>4326</td> <td>WGS 84</td></tr>
892     * </table>
893     *
894     * @param  code  value allocated by authority.
895     * @return the coordinate reference system for the given code.
896     * @throws FactoryException if the object creation failed.
897     */
898    @Override
899    public CoordinateReferenceSystem createCoordinateReferenceSystem(final String code) throws FactoryException {
900        final int id = parseCode(code);
901        switch (id) {
902            case 4326: return createGeographicCRS(code);
903            default:   throw noSuchAuthorityCode(id, code);
904        }
905    }
906
907    /**
908     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
909     *
910     * @throws FactoryException if this method cannot provide the requested information.
911     */
912    @Override
913    public CompoundCRS createCompoundCRS(String code) throws FactoryException {
914        final int id = parseCode(code);
915        switch (id) {
916            default:   throw noSuchAuthorityCode(id, code);
917        }
918    }
919
920    /**
921     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
922     *
923     * @throws FactoryException if this method cannot provide the requested information.
924     */
925    @Override
926    public DerivedCRS createDerivedCRS(String code) throws FactoryException {
927        final int id = parseCode(code);
928        switch (id) {
929            default:   throw noSuchAuthorityCode(id, code);
930        }
931    }
932
933    /**
934     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
935     *
936     * @throws FactoryException if this method cannot provide the requested information.
937     */
938    @Override
939    public EngineeringCRS createEngineeringCRS(String code) throws FactoryException {
940        final int id = parseCode(code);
941        switch (id) {
942            default:   throw noSuchAuthorityCode(id, code);
943        }
944    }
945
946    /**
947     * Returns a {@linkplain GeographicCRS geographic coordinate reference system} from a code.
948     *
949     * <table class="ogc">
950     *   <caption>Supported codes</caption>
951     *   <tr><th>Code</th> <th>Name</th></tr>
952     *   <tr><td>4326</td> <td>WGS 84</td></tr>
953     *   <tr><td>4284</td> <td>Pulkovo 1942</td></tr>
954     * </table>
955     *
956     * @param  code  value allocated by authority.
957     * @return the coordinate reference system for the given code.
958     * @throws FactoryException if the object creation failed.
959     */
960    @Override
961    public GeographicCRS createGeographicCRS(final String code) throws FactoryException {
962        final String name;
963        final int datum;
964        final int coordinateSystem;
965        final int id = parseCode(code);
966        switch (id) {
967            case 4326: name="WGS 84";       datum=6326; coordinateSystem=6422; break;
968            case 4284: name="Pulkovo 1942"; datum=6284; coordinateSystem=6422; break;
969            default:   throw noSuchAuthorityCode(id, code);
970        }
971        assumeTrue(crsFactory != null, NO_CRS_FACTORY);
972        final GeographicCRS object = crsFactory.createGeographicCRS(createPropertiesMap(id, name),
973                createGeodeticDatum(String.valueOf(datum)),
974                createEllipsoidalCS(String.valueOf(coordinateSystem)));
975        validators.validate(object);
976        return object;
977    }
978
979    /**
980     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
981     *
982     * @throws FactoryException if this method cannot provide the requested information.
983     */
984    @Override
985    @Deprecated(since = "3.1")
986    public GeocentricCRS createGeocentricCRS(String code) throws FactoryException {
987        final int id = parseCode(code);
988        switch (id) {
989            default:   throw noSuchAuthorityCode(id, code);
990        }
991    }
992
993    /**
994     * The default implementation delegates to {@link #createGeographicCRS(String)}.
995     *
996     * @throws FactoryException if this method cannot provide the requested information.
997     */
998    @Override
999    public GeodeticCRS createGeodeticCRS(final String code) throws FactoryException {
1000        return createGeographicCRS(code);
1001    }
1002
1003    /**
1004     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
1005     *
1006     * @throws FactoryException if this method cannot provide the requested information.
1007     *
1008     * @deprecated {@code ImageCRS} is replaced by {@link EngineeringCRS} as of ISO 19111:2019.
1009     */
1010    @Override
1011    @Deprecated(since="3.1")
1012    public ImageCRS createImageCRS(String code) throws FactoryException {
1013        final int id = parseCode(code);
1014        switch (id) {
1015            default:   throw noSuchAuthorityCode(id, code);
1016        }
1017    }
1018
1019    /**
1020     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
1021     *
1022     * @throws FactoryException if this method cannot provide the requested information.
1023     */
1024    @Override
1025    public ProjectedCRS createProjectedCRS(String code) throws FactoryException {
1026        final int id = parseCode(code);
1027        switch (id) {
1028            default:   throw noSuchAuthorityCode(id, code);
1029        }
1030    }
1031
1032    /**
1033     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
1034     *
1035     * @throws FactoryException if this method cannot provide the requested information.
1036     */
1037    @Override
1038    public TemporalCRS createTemporalCRS(String code) throws FactoryException {
1039        final int id = parseCode(code);
1040        switch (id) {
1041            default:   throw noSuchAuthorityCode(id, code);
1042        }
1043    }
1044
1045    /**
1046     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
1047     *
1048     * @throws FactoryException if this method cannot provide the requested information.
1049     */
1050    @Override
1051    public VerticalCRS createVerticalCRS(String code) throws FactoryException {
1052        final int id = parseCode(code);
1053        switch (id) {
1054            default:   throw noSuchAuthorityCode(id, code);
1055        }
1056    }
1057
1058    /**
1059     * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally.
1060     *
1061     * @throws FactoryException if this method cannot provide the requested information.
1062     */
1063    @Override
1064    public ParametricCRS createParametricCRS(String code) throws FactoryException {
1065        final int id = parseCode(code);
1066        switch (id) {
1067            default:   throw noSuchAuthorityCode(id, code);
1068        }
1069    }
1070
1071
1072
1073    ///////////////////////////////////////////////////////////////////////////////////////////////
1074    ////////////////                                                               ////////////////
1075    ////////////////    C O O R D I N A T E   O P E R A T I O N   F A C T O R Y    ////////////////
1076    ////////////////                                                               ////////////////
1077    ///////////////////////////////////////////////////////////////////////////////////////////////
1078
1079    /**
1080     * Returns the parameters to use for creating the {@linkplain CoordinateOperation coordinate
1081     * operation} identified by the given EPSG code. The coordinate operation is typically a map
1082     * projection used by exactly one {@linkplain ProjectedCRS projected CRS}, which is listed in
1083     * the second column for information purpose.
1084     *
1085     * <p>The supported codes are determined from the set of examples published in the EPSG guidance
1086     * document, augmented with other sources (IGNF).
1087     * The following table lists the supported codes.
1088     * <i>Codes in italics are not official EPSG codes.</i></p>
1089     *
1090     * <table class="ogc">
1091     *   <caption>Supported codes</caption>
1092     *   <tr><th>Code</th>  <th>Used by CRS</th><th>CRS or transformation name</th>                 <th>Operation method</th></tr>
1093     *   <tr><td>19905</td> <td>3002</td>  <td>Makassar / NEIEZ</td>                                <td>Mercator (variant A)</td></tr>
1094     *   <tr><td>19884</td> <td>3388</td>  <td>Pulkovo 1942 / Caspian Sea Mercator</td>             <td>Mercator (variant B)</td></tr>
1095     *   <tr><td>3856</td>  <td>3857</td>  <td>WGS 84 / Pseudo-Mercator</td>                        <td>Popular Visualisation Pseudo Mercator</td></tr>
1096     *   <tr><td>4085</td>  <td>4087</td>  <td>WGS 84 / World Equidistant Cylindrical</td>          <td>Equidistant Cylindrical</td></tr>
1097     *   <tr><td><i>310642901</i></td> <td><i>310642901</i></td> <td>IGNF:MILLER</td>               <td><i>Miller_Cylindrical</i></td></tr>
1098     *   <tr><td>19958</td> <td>29873</td> <td>Timbalai 1948 / RSO Borneo (m)</td>                  <td>Hotine Oblique Mercator (variant B)</td></tr>
1099     *   <tr><td>19916</td> <td>27700</td> <td>OSGB 1936 / British National Grid</td>               <td>Transverse Mercator</td></tr>
1100     *   <tr><td>17529</td> <td>2053</td>  <td>South African Survey Grid zone 29</td>               <td>Transverse Mercator</td></tr>
1101     *   <tr><td>19975</td> <td>2314</td>  <td>Trinidad 1903 / Trinidad Grid</td>                   <td>Cassini-Soldner</td></tr>
1102     *   <tr><td>19878</td> <td>3139</td>  <td>Vanua Levu 1915 / Vanua Levu Grid</td>               <td>Hyperbolic Cassini-Soldner</td></tr>
1103     *   <tr><td>19910</td> <td>24200</td> <td>JAD69 / Jamaica National Grid</td>                   <td>Lambert Conic Conformal (1SP)</td></tr>
1104     *   <tr><td>14204</td> <td>32040</td> <td>NAD27 / Texas South Central</td>                     <td>Lambert Conic Conformal (2SP)</td></tr>
1105     *   <tr> <td>6198</td> <td>6201</td>  <td>Michigan CS27 Central zone</td>                      <td>Lambert Conic Conformal (2SP Michigan)</td></tr>
1106     *   <tr><td>19902</td> <td>31300</td> <td>Belge 1972 / Belge Lambert 72</td>                   <td>Lambert Conic Conformal (2SP Belgium)</td></tr>
1107     *   <tr><td>19986</td> <td>3035</td>  <td>ETRS89 / LAEA Europe</td>                            <td>Lambert Azimuthal Equal Area</td></tr>
1108     *   <tr><td>16061</td> <td>5041</td>  <td>WGS 84 / UPS North (E,N)</td>                        <td>Polar Stereographic (variant A)</td></tr>
1109     *   <tr><td>19993</td> <td>3032</td>  <td>WGS 84 / Australian Antarctic Polar</td>             <td>Polar Stereographic (variant B)</td></tr>
1110     *   <tr><td>19983</td> <td>2985</td>  <td>Petrels 1972 / Terre Adelie Polar Stereographic</td> <td>Polar Stereographic (variant C)</td></tr>
1111     *   <tr><td>19914</td> <td>28992</td> <td>Amersfoort / RD New</td>                             <td>Oblique Stereographic</td></tr>
1112     *   <tr><td><i>9818</i></td> <td><i>9818</i></td> <td><i>Polyconic</i></td>                    <td><i>Polyconic</i></td></tr>
1113     *   <tr><td><i>9840</i></td> <td><i>9840</i></td> <td><i>Orthographic</i></td>                 <td><i>Orthographic</i></td></tr>
1114     *   <tr><td>15399</td> <td>3295</td>  <td>Guam 1963 / Yap Islands</td>                         <td>Modified Azimuthal Equidistant</td></tr>
1115     *   <tr><td>19952</td> <td>2065</td>  <td>CRS S-JTSK (Ferro) / Krovak</td>                     <td>Krovak</td></tr>
1116     *   <tr><td><i>9605</i></td> <td>4230</td> <td>ED50 to WGS 84</td>                             <td>Abridged Molodensky</td></tr>
1117     *   <tr><td>15595</td> <td>5820</td>  <td>EPSG topocentric example B</td>                      <td>Geocentric/topocentric conversions</td></tr>
1118     * </table>
1119     *
1120     * @param  code  the EPSG code of the {@linkplain CoordinateOperation coordinate operation} to create.
1121     * @return the coordinate operation (typically a map projection) parameters.
1122     * @throws FactoryException if the given EPSG code is unknown to this factory.
1123     *
1124     * @see ParameterizedTransformTest
1125     * @see AuthorityFactoryTest
1126     */
1127    protected ParameterValueGroup createParameters(final int code) throws FactoryException {
1128        final ParameterValueGroup parameters = createParameters(mtFactory, code);
1129        validators.validate(parameters);
1130        return parameters;
1131    }
1132
1133    /**
1134     * Implementation of the above {@link #createParameters(int)} method,
1135     * as a static method for direct access by {@link ParameterizedTransformTest}.
1136     *
1137     * @param  factory  the factory to use for creating the parameters.
1138     * @param  code     authority code of the parameter to create.
1139     * @return parameter for the given authority code.
1140     * @throws FactoryException if the given EPSG code is unknown to this factory.
1141     */
1142    static ParameterValueGroup createParameters(final MathTransformFactory factory, final int code)
1143            throws FactoryException
1144    {
1145        final ParameterValueGroup parameters;
1146        switch (code) {
1147            case 19905: {       // "Makassar / NEIEZ" using operation method 9804
1148                parameters = factory.getDefaultParameters("Mercator (variant A)");                      // Alias "Mercator (1SP)"
1149                parameters.parameter("semi_major").setValue(6377397.155);                               // Bessel 1841
1150                parameters.parameter("semi_minor").setValue(6377397.155 * (1 - 1/299.1528128));
1151                parameters.parameter("Latitude of natural origin")    .setValue(  0.0);
1152                parameters.parameter("Longitude of natural origin")   .setValue(110.0);
1153                parameters.parameter("Scale factor at natural origin").setValue(0.997);
1154                parameters.parameter("False easting").setValue(3900000.0);
1155                parameters.parameter("False northing").setValue(900000.0);
1156                break;
1157            }
1158            case 19884: {       // "Pulkovo 1942 / Caspian Sea Mercator" using operation method 9805
1159                parameters = factory.getDefaultParameters("Mercator (variant B) ");                     // Alias "Mercator (2SP)"
1160                parameters.parameter("semi_major").setValue(6378245.0);                                 // Krassowski 1940
1161                parameters.parameter("semi_minor").setValue(6378245.0 * (1 - 1/298.3));
1162                parameters.parameter("Latitude of 1st standard parallel").setValue(42.0);
1163                parameters.parameter("Longitude of natural origin")      .setValue(51.0);
1164                break;
1165            }
1166            case 3856: {        // "WGS 84 / Pseudo-Mercator" using operation method 1024
1167                parameters = factory.getDefaultParameters("Popular Visualisation Pseudo Mercator");
1168                parameters.parameter("semi_major").setValue(6378137.0);                                 // WGS 84
1169                parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236));
1170                break;
1171            }
1172            case 4085: {        // "WGS 84 / World Equidistant Cylindrical" using operation method 1028
1173                parameters = factory.getDefaultParameters("Equidistant Cylindrical");
1174                parameters.parameter("semi_major").setValue(6378137.0);                                 // WGS 84
1175                parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236));
1176                break;
1177            }
1178            case 310642901: {   // "IGNF:MILLER" (not an official EPSG code)
1179                parameters = factory.getDefaultParameters("Miller_Cylindrical");
1180                parameters.parameter("semi_major").setValue(6378137.0);
1181                parameters.parameter("semi_minor").setValue(6378137.0);
1182                break;
1183            }
1184            case 19958: {       // "Rectified Skew Orthomorphic Borneo Grid (metres)" using operation method 9815
1185                parameters = factory.getDefaultParameters("Hotine Oblique Mercator (variant B)");
1186                parameters.parameter("semi_major").setValue(6377298.556);                               // Everest 1830
1187                parameters.parameter("semi_minor").setValue(6377298.556 * (1 - 1/300.8017));
1188                parameters.parameter("Latitude of projection centre") .setValue(  4.0);                         //   4°00'00"N
1189                parameters.parameter("Longitude of projection centre").setValue(115.0);                         // 115°00'00"E
1190                parameters.parameter("Azimuth of initial line").setValue(53 + (18 + 56.9537/60)/60);            //  53°18'56.9537"
1191                parameters.parameter("Angle from Rectified to Skew Grid").setValue(53 + (7 + 48.3685/60)/60);   //  53°07'48.3685"
1192                parameters.parameter("Scale factor on initial line") .setValue(0.99984);
1193                parameters.parameter("Easting at projection centre") .setValue(590476.87);
1194                parameters.parameter("Northing at projection centre").setValue(442857.65);
1195                break;
1196            }
1197            case 19916: {       // "British National Grid" using operation method 9807
1198                parameters = factory.getDefaultParameters("Transverse Mercator");
1199                parameters.parameter("semi_major").setValue(6377563.396);                               // Airy
1200                parameters.parameter("semi_minor").setValue(6377563.396 * (1 - 1/299.32496));
1201                parameters.parameter("Latitude of natural origin") .setValue(49.0);
1202                parameters.parameter("Longitude of natural origin").setValue(-2.0);
1203                parameters.parameter("Scale factor at natural origin").setValue(0.9996012717);
1204                parameters.parameter("False easting") .setValue( 400000.00);
1205                parameters.parameter("False northing").setValue(-100000.00);
1206                break;
1207            }
1208            case 17529: {       // "South African Survey Grid zone 29" using operation method 9808
1209                parameters = factory.getDefaultParameters("Transverse Mercator (South Orientated)");
1210                parameters.parameter("semi_major").setValue(6378137.0);                                 // WGS 84
1211                parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236));
1212                parameters.parameter("Latitude of natural origin").setValue(0.0);
1213                parameters.parameter("Longitude of natural origin").setValue(29.0);
1214                parameters.parameter("Scale factor at natural origin").setValue(1.0);
1215                parameters.parameter("False easting") .setValue(0.0);
1216                parameters.parameter("False northing").setValue(0.0);
1217                break;
1218            }
1219            case 19975: {       // "Trinidad 1903 / Trinidad Grid" using operation method 9806
1220                /*
1221                 * Values used below are those published in IOGP Publication 373-7-2 §3.2.2 — September 2019.
1222                 * They differ from values in EPSG geodetic dataset 9.8.11 in following aspects: geodetic dataset
1223                 * uses Clarke's foot units instead of foot and link units, with same values for ellipsoid axis
1224                 * lengths (before conversion) but different values for false easting and northing parameters.
1225                 * The differences in easting/northing parameters is up to 0.8 metre. We keep the values published
1226                 * in IOGP 373-7-2 because the sample point tested in map projection is computed with those values.
1227                 */
1228                parameters = factory.getDefaultParameters("Cassini-Soldner");
1229                parameters.parameter("semi_major").setValue(20926348.0 * FEET);                         // Clarke 1858
1230                parameters.parameter("semi_minor").setValue(20855233.0 * FEET);
1231                parameters.parameter("Latitude of natural origin") .setValue(10 + (26 + 30.0/60)/60);   // 10°26'30"N
1232                parameters.parameter("Longitude of natural origin").setValue(-(61 + 20.0/60));          // 61°20'00"W
1233                parameters.parameter("False easting") .setValue(430000.00 * LINKS);
1234                parameters.parameter("False northing").setValue(325000.00 * LINKS);
1235                break;
1236            }
1237            case 19878: {       // "Vanua Levu Grid" using operation method 9833
1238                parameters = factory.getDefaultParameters("Hyperbolic Cassini-Soldner");
1239                parameters.parameter("semi_major").setValue(20926202.0 * FEET);                         // Clarke 1880
1240                parameters.parameter("semi_minor").setValue(20854895.0 * FEET);
1241                parameters.parameter("Latitude of natural origin") .setValue(-(16 + 15./60));           // 16°15'00"S
1242                parameters.parameter("Longitude of natural origin").setValue( 179 + 20./60);            // 179°20'00"E
1243                parameters.parameter("False easting") .setValue(1251331.8 * LINKS);
1244                parameters.parameter("False northing").setValue(1662888.5 * LINKS);
1245                break;
1246            }
1247            case 19910: {       // "JAD69 / Jamaica National Grid" using operation method 9801
1248                parameters = factory.getDefaultParameters("Lambert Conic Conformal (1SP)");
1249                parameters.parameter("semi_major").setValue(6378206.4);                                 // Clarke 1866
1250                parameters.parameter("semi_minor").setValue(6356583.8);
1251                parameters.parameter("Latitude of natural origin")    .setValue( 18.0);
1252                parameters.parameter("Longitude of natural origin")   .setValue(-77.0);
1253                parameters.parameter("Scale factor at natural origin").setValue(  1.0);
1254                parameters.parameter("False easting") .setValue(250000.00);
1255                parameters.parameter("False northing").setValue(150000.00);
1256                break;
1257            }
1258            case 14204: {       // "NAD27 / Texas South Central" using operation method 9802
1259                parameters = factory.getDefaultParameters("Lambert Conic Conformal (2SP)");
1260                parameters.parameter("semi_major").setValue(6378206.4);                                 // Clarke 1866
1261                parameters.parameter("semi_minor").setValue(6356583.8);
1262                parameters.parameter("Latitude of 1st standard parallel").setValue(28 + 23.0/60);       // 28°23'00"N
1263                parameters.parameter("Latitude of 2nd standard parallel").setValue(30 + 17.0/60);       // 30°17'00"N
1264                parameters.parameter("Latitude of false origin")         .setValue(27 + 50.0/60);       // 27°50'00"N
1265                parameters.parameter("Longitude of false origin")        .setValue(-99.0);              // 99°00'00"W
1266                parameters.parameter("Easting at false origin") .setValue(2000000 / R_US_FEET);
1267                parameters.parameter("Northing at false origin").setValue(      0 / R_US_FEET);
1268                break;
1269            }
1270            case 6198: {        // "Michigan CS27 Central zone" using operation method 1051
1271                parameters = factory.getDefaultParameters("Lambert Conic Conformal (2SP Michigan)");
1272                parameters.parameter("semi_major").setValue(6378206.4);                                 // Clarke 1866
1273                parameters.parameter("semi_minor").setValue(6356583.8);
1274                parameters.parameter("Latitude of 1st standard parallel").setValue( 44 + 11.0/60);      // 44°11' N
1275                parameters.parameter("Latitude of 2nd standard parallel").setValue( 45 + 42.0/60);      // 45°42' N
1276                parameters.parameter("Latitude of false origin")         .setValue( 43 + 19.0/60);      // 43°19' N
1277                parameters.parameter("Longitude of false origin")        .setValue(-84 - 20.0/60);      // 84°20' W
1278                parameters.parameter("Easting at false origin") .setValue(2000000 / R_US_FEET);
1279                parameters.parameter("Northing at false origin").setValue(      0 / R_US_FEET);
1280                parameters.parameter("Ellipsoid scaling factor").setValue(1.0000382);
1281                break;
1282            }
1283            case 19902: {       // "Belge 1972 / Belge Lambert 72" using operation method 9803
1284                parameters = factory.getDefaultParameters("Lambert Conic Conformal (2SP Belgium)");
1285                parameters.parameter("semi_major").setValue(6378388.0);                                 // International 1924
1286                parameters.parameter("semi_minor").setValue(6378388.0 * (1 - 1/297.0));
1287                parameters.parameter("Latitude of 1st standard parallel").setValue(49 + 50.0/60);       // 49°50'00.000"N
1288                parameters.parameter("Latitude of 2nd standard parallel").setValue(51 + 10.0/60);       // 51°10'00.000"N
1289                parameters.parameter("Latitude of false origin")         .setValue(90.0);               // 90°00'00.000"N
1290                parameters.parameter("Longitude of false origin").setValue(4 + (21 + 24.983/60)/60);    //  4°21'24.983"E
1291                parameters.parameter("Easting at false origin") .setValue( 150000.01);
1292                parameters.parameter("Northing at false origin").setValue(5400088.44);
1293                break;
1294            }
1295            case 19986: {       // "Europe Equal Area 2001" using operation method 9820
1296                parameters = factory.getDefaultParameters("Lambert Azimuthal Equal Area");
1297                parameters.parameter("semi_major").setValue(6378137.0);
1298                parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572221));
1299                parameters.parameter("Latitude of natural origin") .setValue(52.0);
1300                parameters.parameter("Longitude of natural origin").setValue(10.0);
1301                parameters.parameter("False easting") .setValue(4321000.00);
1302                parameters.parameter("False northing").setValue(3210000.00);
1303                break;
1304            }
1305            case 16061: {       // "Universal Polar Stereographic North" using operation method 9810
1306                parameters = factory.getDefaultParameters("Polar Stereographic (variant A)");
1307                parameters.parameter("semi_major").setValue(6378137.0);                                 // WGS84
1308                parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236));
1309                parameters.parameter("Latitude of natural origin").setValue(90.0);
1310                parameters.parameter("Longitude of natural origin").setValue(0.0);
1311                parameters.parameter("Scale factor at natural origin").setValue(0.994);
1312                parameters.parameter("False easting") .setValue(2000000.00);
1313                parameters.parameter("False northing").setValue(2000000.00);
1314                break;
1315            }
1316            case 19993: {       // "Australian Antarctic Polar Stereographic" using operation method 9829
1317                parameters = factory.getDefaultParameters("Polar Stereographic (variant B)");
1318                parameters.parameter("semi_major").setValue(6378137.0);                                 // WGS84
1319                parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236));
1320                parameters.parameter("Latitude of standard parallel").setValue(-71.0);
1321                parameters.parameter("Longitude of origin").setValue(70.0);
1322                parameters.parameter("False easting") .setValue(6000000.00);
1323                parameters.parameter("False northing").setValue(6000000.00);
1324                break;
1325            }
1326            case 19983: {       // "Petrels 1972 / Terre Adelie Polar Stereographic" using operation method 9830
1327                parameters = factory.getDefaultParameters("Polar Stereographic (variant C)");
1328                parameters.parameter("semi_major").setValue(6378388.0);                                 // International 1924
1329                parameters.parameter("semi_minor").setValue(6378388.0 * (1 - 1/297.0));
1330                parameters.parameter("Latitude of standard parallel").setValue(-67.0);
1331                parameters.parameter("Longitude of origin").setValue(140.0);
1332                parameters.parameter("Easting at false origin") .setValue(300000.00);
1333                parameters.parameter("Northing at false origin").setValue(200000.00);
1334                break;
1335            }
1336            case 19914: {       // "RD New" using operation method 9809
1337                parameters = factory.getDefaultParameters("Oblique Stereographic");
1338                parameters.parameter("semi_major").setValue(6377397.155);                               // Bessel 1841
1339                parameters.parameter("semi_minor").setValue(6377397.155 * (1 - 1/299.15281));
1340                parameters.parameter("Latitude of natural origin").setValue(52 + ( 9 + 22.178/60)/60);  // 52°09'22.178"N
1341                parameters.parameter("Longitude of natural origin").setValue(5 + (23 + 15.500/60)/60);  //  5°23'15.500"E
1342                parameters.parameter("Scale factor at natural origin").setValue(0.9999079);
1343                parameters.parameter("False easting") .setValue(155000.00);
1344                parameters.parameter("False northing").setValue(463000.00);
1345                break;
1346            }
1347            case 9818: {        // (not an official EPSG code) using operation method 9818
1348                parameters = factory.getDefaultParameters("Polyconic");
1349                parameters.parameter("semi_major").setValue(6378206.4);
1350                parameters.parameter("semi_minor").setValue(6356583.8);
1351                parameters.parameter("Latitude of natural origin") .setValue(0.0);
1352                parameters.parameter("Longitude of natural origin").setValue(0.0);
1353                parameters.parameter("False easting") .setValue(0.0);
1354                parameters.parameter("False northing").setValue(0.0);
1355                break;
1356            }
1357            case 9840: {        // (not an official EPSG code) using operation method 9840
1358                parameters = factory.getDefaultParameters("Orthographic");
1359                parameters.parameter("semi_major").setValue(6378137.0);                                 // WGS84
1360                parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236));
1361                parameters.parameter("Latitude of natural origin").setValue(55.0);
1362                parameters.parameter("Longitude of natural origin").setValue(5.0);
1363                parameters.parameter("False easting") .setValue(0.0);
1364                parameters.parameter("False northing").setValue(0.0);
1365                break;
1366            }
1367            case 15399: {       // "Guam 1963 / Yap Islands" using operation method 9832
1368                parameters = factory.getDefaultParameters("Modified Azimuthal Equidistant");
1369                parameters.parameter("semi_major").setValue(6378206.4);                                 // Clarke 1866
1370                parameters.parameter("semi_minor").setValue(6356583.8);
1371                parameters.parameter("Latitude of natural origin").setValue(9 + (32 + 48.15/60)/60);
1372                parameters.parameter("Longitude of natural origin").setValue(138 + (10 + 7.48/60)/60);
1373                parameters.parameter("False easting") .setValue(40000.0);
1374                parameters.parameter("False northing").setValue(60000.0);
1375                break;
1376            }
1377            case 19952: {       // "CRS S-JTSK (Ferro) / Krovak" using operation method 9819
1378                parameters = factory.getDefaultParameters("Krovak");
1379                parameters.parameter("semi_major").setValue(6377397.155);                               // Bessel
1380                parameters.parameter("semi_minor").setValue(6377397.155 * (1 - 1/299.15281));
1381                parameters.parameter("Latitude of projection centre").setValue(49.5);                   // 49°30'00"N
1382                parameters.parameter("Longitude of origin").setValue(24 + 50.0/60);                     // 24°30'00"E
1383                parameters.parameter("Co-latitude of cone axis").setValue(30 + (17 + 17.3031/60)/60);
1384                parameters.parameter("Latitude of pseudo standard parallel").setValue(78.5);
1385                parameters.parameter("Scale factor on pseudo standard parallel").setValue(0.99990);
1386                break;
1387            }
1388            case 9605: {        // (not an official EPSG code) using operation method 9605
1389                parameters = factory.getDefaultParameters("Abridged Molodensky");
1390                parameters.parameter("dim").setValue(3);                                                // Parameter defined by OGC 01-009
1391                parameters.parameter("src_semi_major").setValue(6378137.0);                             // WGS84
1392                parameters.parameter("src_semi_minor").setValue(6378137.0 * (1 - 1/298.2572236));
1393                parameters.parameter("X-axis translation").setValue( 84.87);
1394                parameters.parameter("Y-axis translation").setValue( 96.49);
1395                parameters.parameter("Z-axis translation").setValue(116.95);
1396                parameters.parameter("Semi-major axis length difference").setValue(251);
1397                parameters.parameter("Flattening difference").setValue(1.41927E-05);
1398                break;
1399            }
1400            case 15594: {       // EPSG topocentric example A
1401                parameters = factory.getDefaultParameters("Geographic/topocentric conversions");
1402                parameters.parameter("semi_major").setValue(6378137.0);                               // WGS84
1403                parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236));
1404                parameters.parameter("Latitude of topocentric origin").setValue(55);
1405                parameters.parameter("Longitude of topocentric origin").setValue(5);
1406                parameters.parameter("Ellipsoidal height of topocentric origin").setValue(200);
1407                break;
1408            }
1409            case 15595: {       // EPSG topocentric example B
1410                parameters = factory.getDefaultParameters("Geocentric/topocentric conversions");
1411                parameters.parameter("semi_major").setValue(6378137.0);                               // WGS84
1412                parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236));
1413                parameters.parameter("Geocentric X of topocentric origin").setValue(3652755.3058);
1414                parameters.parameter("Geocentric Y of topocentric origin").setValue( 319574.6799);
1415                parameters.parameter("Geocentric Z of topocentric origin").setValue(5201547.3536);
1416                break;
1417            }
1418            default: {
1419                throw noSuchAuthorityCode(code, String.valueOf(code));
1420            }
1421        }
1422        return parameters;
1423    }
1424}