/**
  * Tests all map projection creation.
  *
  * @throws FactoryException If a CRS can not be created.
  */
 @Test
 public void testMapProjections() throws FactoryException {
   out.println();
   out.println("Testing classification names");
   out.println("----------------------------");
   final CRSFactory crsFactory = ReferencingFactoryFinder.getCRSFactory(null);
   final MathTransformFactory mtFactory = ReferencingFactoryFinder.getMathTransformFactory(null);
   final Collection<OperationMethod> methods = mtFactory.getAvailableMethods(Projection.class);
   final Map<String, ?> dummyName = Collections.singletonMap("name", "Test");
   for (final OperationMethod method : methods) {
     final String classification = method.getName().getCode();
     final ParameterValueGroup param = mtFactory.getDefaultParameters(classification);
     try {
       param.parameter("semi_major").setValue(6377563.396);
       param.parameter("semi_minor").setValue(6356256.909237285);
     } catch (IllegalArgumentException e) {
       // Above parameters do not exists. Ignore.
     }
     final MathTransform mt;
     try {
       mt = mtFactory.createParameterizedTransform(param);
     } catch (FactoryException e) {
       // Probably not a map projection. This test is mostly about projection, so ignore.
       continue;
     } catch (UnsupportedOperationException e) {
       continue;
     }
     if (mt instanceof MapProjection) {
       /*
        * Tests map projection properties. Some tests are ommitted for south-oriented
        * map projections, since they are implemented as a concatenation of their North-
        * oriented variants with an affine transform.
        */
       out.println(classification);
       final boolean skip =
           classification.equalsIgnoreCase("Transverse Mercator (South Orientated)")
               || classification.equalsIgnoreCase("Equidistant_Cylindrical");
       if (!skip) {
         assertEquals(
             classification, ((MapProjection) mt).getParameterDescriptors().getName().getCode());
       }
       final ProjectedCRS projCRS =
           crsFactory.createProjectedCRS(
               dummyName,
               DefaultGeographicCRS.WGS84,
               new DefiningConversion(dummyName, method, mt),
               DefaultCartesianCS.PROJECTED);
       final Conversion conversion = projCRS.getConversionFromBase();
       assertSame(mt, conversion.getMathTransform());
       final OperationMethod projMethod = conversion.getMethod();
       assertEquals(classification, projMethod.getName().getCode());
     }
   }
 }
  /**
   * Tests the creation of new coordinate reference systems.
   *
   * @throws FactoryException if a coordinate reference system can't be created.
   */
  @Test
  public void testCreation() throws FactoryException {
    out.println();
    out.println("Testing CRS creations");
    out.println("---------------------");
    out.println();
    out.println("create Coodinate Reference System....1: ");
    final DatumFactory datumFactory = ReferencingFactoryFinder.getDatumFactory(null);
    final CSFactory csFactory = ReferencingFactoryFinder.getCSFactory(null);
    final CRSFactory crsFactory = ReferencingFactoryFinder.getCRSFactory(null);
    final MathTransformFactory mtFactory = ReferencingFactoryFinder.getMathTransformFactory(null);

    final Ellipsoid airy1830;
    final Unit<Length> meters = SI.METER;
    airy1830 = datumFactory.createEllipsoid(name("Airy1830"), 6377563.396, 6356256.910, meters);
    out.println();
    out.println("create Coodinate Reference System....2: ");
    out.println(airy1830.toWKT());

    final PrimeMeridian greenwich;
    final Unit<Angle> degrees = NonSI.DEGREE_ANGLE;
    greenwich = datumFactory.createPrimeMeridian(name("Greenwich"), 0, degrees);
    out.println();
    out.println("create Coodinate Reference System....3: ");
    out.println(greenwich.toWKT());

    // NOTE: we could use the following pre-defined constant instead:
    //       DefaultPrimeMeridian.GREENWICH;
    final GeodeticDatum datum;
    datum = datumFactory.createGeodeticDatum(name("Airy1830"), airy1830, greenwich);
    out.println();
    out.println("create Coodinate Reference System....4: ");
    out.println(datum.toWKT());

    // NOTE: we could use the following pre-defined constant instead:
    //       DefaultEllipsoidalCS.GEODETIC_2D;
    final EllipsoidalCS ellCS;
    ellCS =
        csFactory.createEllipsoidalCS(
            name("Ellipsoidal"),
            csFactory.createCoordinateSystemAxis(
                name("Longitude"), "long", AxisDirection.EAST, degrees),
            csFactory.createCoordinateSystemAxis(
                name("Latitude"), "lat", AxisDirection.NORTH, degrees));
    out.println();
    out.println("create Coodinate Reference System....5: ");
    out.println(ellCS); // No WKT for coordinate systems

    final GeographicCRS geogCRS;
    geogCRS = crsFactory.createGeographicCRS(name("Airy1830"), datum, ellCS);
    out.println();
    out.println("create Coodinate Reference System....6: ");
    out.println(geogCRS.toWKT());

    final MathTransform p;
    final ParameterValueGroup param = mtFactory.getDefaultParameters("Transverse_Mercator");
    param.parameter("semi_major").setValue(airy1830.getSemiMajorAxis());
    param.parameter("semi_minor").setValue(airy1830.getSemiMinorAxis());
    param.parameter("central_meridian").setValue(49);
    param.parameter("latitude_of_origin").setValue(-2);
    param.parameter("false_easting").setValue(400000);
    param.parameter("false_northing").setValue(-100000);
    out.println();
    out.println("create Coodinate System....7: ");
    out.println(param);

    // NOTE: we could use the following pre-defined constant instead:
    //       DefaultCartesianCS.PROJECTED;
    final CartesianCS cartCS;
    cartCS =
        csFactory.createCartesianCS(
            name("Cartesian"),
            csFactory.createCoordinateSystemAxis(name("Easting"), "x", AxisDirection.EAST, meters),
            csFactory.createCoordinateSystemAxis(
                name("Northing"), "y", AxisDirection.NORTH, meters));
    out.println();
    out.println("create Coodinate Reference System....8: ");
    out.println(cartCS); // No WKT for coordinate systems

    final Hints hints = new Hints();
    hints.put(Hints.DATUM_FACTORY, datumFactory);
    hints.put(Hints.CS_FACTORY, csFactory);
    hints.put(Hints.CRS_FACTORY, crsFactory);
    hints.put(Hints.MATH_TRANSFORM_FACTORY, mtFactory);

    final ReferencingFactoryContainer container = new ReferencingFactoryContainer(hints);
    assertSame(datumFactory, container.getDatumFactory());
    assertSame(csFactory, container.getCSFactory());
    assertSame(crsFactory, container.getCRSFactory());
    assertSame(mtFactory, container.getMathTransformFactory());

    final Conversion conversion = new DefiningConversion("GBN grid", param);
    final ProjectedCRS projCRS =
        crsFactory.createProjectedCRS(
            name("Great_Britian_National_Grid"), geogCRS, conversion, cartCS);
    out.println();
    out.println("create Coodinate System....9: ");
    out.println(projCRS.toWKT());
  }
  /**
   * Tests datum aliases. Note: ellipsoid and prime meridian are dummy values just (not conform to
   * the usage in real world) just for testing purpose.
   *
   * @throws FactoryException If a CRS can not be created.
   */
  @Test
  public void testDatumAliases() throws FactoryException {
    final String name0 = "Nouvelle Triangulation Francaise (Paris)";
    final String name1 = "Nouvelle_Triangulation_Francaise_Paris";
    final String name2 = "D_NTF";
    final String name3 = "NTF (Paris meridian)";
    final Ellipsoid ellipsoid = DefaultEllipsoid.WGS84;
    final PrimeMeridian meridian = DefaultPrimeMeridian.GREENWICH;
    DatumFactory factory = new ReferencingObjectFactory();
    final Map<String, ?> properties = Collections.singletonMap("name", name1);
    GeodeticDatum datum = factory.createGeodeticDatum(properties, ellipsoid, meridian);
    assertTrue(datum.getAlias().isEmpty());

    for (int i = 0; i < 3; i++) {
      switch (i) {
        case 0:
          factory = new DatumAliases(factory);
          break;
        case 1:
          factory = ReferencingFactoryFinder.getDatumFactory(null);
          break;
        case 2:
          ((DatumAliases) factory).freeUnused();
          break;
        default:
          throw new AssertionError(); // Should not occurs.
      }
      final String pass = "******" + i;
      datum = factory.createGeodeticDatum(properties, ellipsoid, meridian);
      final GenericName[] aliases = datum.getAlias().toArray(new GenericName[0]);
      assertEquals(pass, 4, aliases.length);
      assertEquals(pass, name0, aliases[0].tip().toString());
      assertEquals(pass, name1, aliases[1].tip().toString());
      assertEquals(pass, name2, aliases[2].tip().toString());
      assertEquals(pass, name3, aliases[3].tip().toString());
      assertTrue(pass, aliases[0] instanceof ScopedName);
      assertTrue(pass, aliases[1] instanceof ScopedName);
      assertTrue(pass, aliases[2] instanceof ScopedName);
      assertTrue(pass, aliases[3] instanceof ScopedName);
    }

    datum =
        factory.createGeodeticDatum(Collections.singletonMap("name", "Tokyo"), ellipsoid, meridian);
    Collection<GenericName> aliases = datum.getAlias();
    assertEquals(4, aliases.size());

    ((DatumAliases) factory).freeUnused();
    datum =
        factory.createGeodeticDatum(
            Collections.singletonMap("name", "_toKyo  _"), ellipsoid, meridian);
    assertEquals(4, datum.getAlias().size());
    assertTrue(aliases.equals(datum.getAlias()));

    datum =
        factory.createGeodeticDatum(
            Collections.singletonMap("name", "D_Tokyo"), ellipsoid, meridian);
    assertEquals(4, datum.getAlias().size());

    datum =
        factory.createGeodeticDatum(
            Collections.singletonMap("name", "Luxembourg 1930"), ellipsoid, meridian);
    assertEquals(3, datum.getAlias().size());

    datum =
        factory.createGeodeticDatum(Collections.singletonMap("name", "Dummy"), ellipsoid, meridian);
    assertTrue("Non existing datum should have no alias.", datum.getAlias().isEmpty());

    datum =
        factory.createGeodeticDatum(
            Collections.singletonMap("name", "WGS 84"), ellipsoid, meridian);
    assertTrue(AbstractIdentifiedObject.nameMatches(datum, "WGS 84"));
    assertTrue(AbstractIdentifiedObject.nameMatches(datum, "WGS_1984"));
    assertTrue(AbstractIdentifiedObject.nameMatches(datum, "World Geodetic System 1984"));
    assertFalse(AbstractIdentifiedObject.nameMatches(datum, "WGS 72"));
  }