/** Tests axis swapping using {@link GeneralMatrix}. */
 @Test
 public void testAxisSwapping() {
   AxisDirection[] srcAxis = {NORTH, EAST, UP};
   AxisDirection[] dstAxis = {NORTH, EAST, UP};
   GeneralMatrix matrix = new GeneralMatrix(srcAxis, dstAxis);
   assertTrue(matrix.isAffine());
   assertTrue(matrix.isIdentity());
   dstAxis = new AxisDirection[] {WEST, UP, SOUTH};
   matrix = new GeneralMatrix(srcAxis, dstAxis);
   assertTrue(matrix.isAffine());
   assertFalse(matrix.isIdentity());
   assertEquals(
       new GeneralMatrix(
           new double[][] {
             {0, -1, 0, 0},
             {0, 0, 1, 0},
             {-1, 0, 0, 0},
             {0, 0, 0, 1}
           }),
       matrix);
   dstAxis = new AxisDirection[] {DOWN, NORTH};
   matrix = new GeneralMatrix(srcAxis, dstAxis);
   assertFalse(matrix.isIdentity());
   assertEquals(
       new GeneralMatrix(
           new double[][] {
             {0, 0, -1, 0},
             {1, 0, 0, 0},
             {0, 0, 0, 1}
           }),
       matrix);
   dstAxis = new AxisDirection[] {DOWN, DOWN};
   matrix = new GeneralMatrix(srcAxis, dstAxis);
   assertFalse(matrix.isIdentity());
   assertEquals(
       new GeneralMatrix(
           new double[][] {
             {0, 0, -1, 0},
             {0, 0, -1, 0},
             {0, 0, 0, 1}
           }),
       matrix);
   dstAxis = new AxisDirection[] {DOWN, GEOCENTRIC_X};
   try {
     matrix = new GeneralMatrix(srcAxis, dstAxis);
     fail();
   } catch (IllegalArgumentException exception) {
     // This is the expected exception (axis not in source).
   }
   srcAxis = dstAxis;
   dstAxis = new AxisDirection[] {NORTH, EAST, UP, WEST};
   try {
     matrix = new GeneralMatrix(srcAxis, dstAxis);
     fail();
   } catch (IllegalArgumentException exception) {
     // This is the expected exception (colinear axis).
   }
 }
 /** Tests matrix inversion and multiplication using {@link Matrix2}. */
 @Test
 public void testMatrix2() {
   final Matrix2 m = new Matrix2();
   assertTrue(m.isAffine());
   assertTrue(m.isIdentity());
   final Random random = new Random(8447482612423035360L);
   final GeneralMatrix identity = new GeneralMatrix(2);
   for (int i = 0; i < 100; i++) {
     m.setElement(0, 0, 100 * random.nextDouble());
     m.setElement(0, 1, 100 * random.nextDouble());
     m.setElement(1, 0, 100 * random.nextDouble());
     m.setElement(1, 1, 100 * random.nextDouble());
     final Matrix2 original = m.clone();
     final GeneralMatrix check = new GeneralMatrix(m);
     m.invert();
     check.invert();
     assertTrue(check.equals(m, 1E-9));
     m.multiply(original);
     assertTrue(identity.equals(m, 1E-9));
   }
 }
  /*
   * Copied from GeoTools GeoTiffMetadata2CRSAdapter because the given tie-point offset is
   * not correctly interpreted in GeoTools. The tie-point should be placed at the pixel center
   * if RasterPixelIsPoint is set as value for GTRasterTypeGeoKey.
   * See links:
   * http://www.remotesensing.org/geotiff/faq.html#PixelIsPoint
   * http://lists.osgeo.org/pipermail/gdal-dev/2007-November/015040.html
   * http://trac.osgeo.org/gdal/wiki/rfc33_gtiff_pixelispoint
   */
  private static MathTransform getRasterToModel(final GeoTiffIIOMetadataDecoder metadata)
      throws GeoTiffException {
    //
    // Load initials
    //
    final boolean hasTiePoints = metadata.hasTiePoints();
    final boolean hasPixelScales = metadata.hasPixelScales();
    final boolean hasModelTransformation = metadata.hasModelTrasformation();
    int rasterType = getGeoKeyAsInt(GeoTiffConstants.GTRasterTypeGeoKey, metadata);
    // geotiff spec says that PixelIsArea is the default
    if (rasterType == GeoTiffConstants.UNDEFINED) {
      rasterType = GeoTiffConstants.RasterPixelIsArea;
    }
    MathTransform xform;
    if (hasTiePoints && hasPixelScales) {

      //
      // we use tie points and pixel scales to build the grid to world
      //
      // model space
      final TiePoint[] tiePoints = metadata.getModelTiePoints();
      final PixelScale pixScales = metadata.getModelPixelScales();

      // here is the matrix we need to build
      final GeneralMatrix gm = new GeneralMatrix(3);
      final double scaleRaster2ModelLongitude = pixScales.getScaleX();
      final double scaleRaster2ModelLatitude = -pixScales.getScaleY();
      // "raster" space
      final double tiePointColumn =
          tiePoints[0].getValueAt(0)
              + (rasterType == GeoTiffConstants.RasterPixelIsPoint ? 0.5 : 0);
      final double tiePointRow =
          tiePoints[0].getValueAt(1)
              + (rasterType == GeoTiffConstants.RasterPixelIsPoint ? 0.5 : 0);

      // compute an "offset and scale" matrix
      gm.setElement(0, 0, scaleRaster2ModelLongitude);
      gm.setElement(1, 1, scaleRaster2ModelLatitude);
      gm.setElement(0, 1, 0);
      gm.setElement(1, 0, 0);

      gm.setElement(
          0, 2, tiePoints[0].getValueAt(3) - (scaleRaster2ModelLongitude * tiePointColumn));
      gm.setElement(1, 2, tiePoints[0].getValueAt(4) - (scaleRaster2ModelLatitude * tiePointRow));

      // make it a LinearTransform
      xform = ProjectiveTransform.create(gm);

    } else if (hasModelTransformation) {
      if (rasterType == GeoTiffConstants.RasterPixelIsPoint) {
        final AffineTransform tempTransform =
            new AffineTransform(metadata.getModelTransformation());
        tempTransform.concatenate(AffineTransform.getTranslateInstance(0.5, 0.5));
        xform = ProjectiveTransform.create(tempTransform);
      } else {
        assert rasterType == GeoTiffConstants.RasterPixelIsArea;
        xform = ProjectiveTransform.create(metadata.getModelTransformation());
      }
    } else {
      throw new GeoTiffException(metadata, "Unknown Raster to Model configuration.", null);
    }

    return xform;
  }