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