/*
 *    Geotools2 - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2002-2006, Geotools Project Managment Committee (PMC)
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotools.referencing.operation.builder;

import org.geotools.referencing.operation.matrix.GeneralMatrix;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.geometry.MismatchedReferenceSystemException;

// J2SE and extensions
import java.util.List;
import javax.vecmath.MismatchedSizeException;


/**
 * Builds {@linkplain org.opengis.referencing.operation.MathTransform
 * MathTransform} setup as Affine transformation from a list of {@linkplain
 * MappedPosition MappedPosition}. The calculation uses least square method.
 * The Affine transform equation:
 * <pre>                                                       
 *  [ x']   [  m00  m01  m02  ] [ x ]   [ m00x + m01y + m02 ]
 *  [ y'] = [  m10  m11  m12  ] [ y ] = [ m10x + m11y + m12 ]
 *  [ 1 ]   [   0    0    1   ] [ 1 ]   [         1         ] 
 *   x' = m * x </pre>In the case that we have more identical points we can
 * write it like this  (in Matrix):<pre> 
 *  [ x'<sub>1</sub> ]      [ x<sub>1</sub> y<sub>1</sub> 1  0  0  0 ]   [ m00 ]
 *  [ x'<sub>2</sub> ]      [ x<sub>2</sub> y<sub>2</sub> 1  0  0  0 ]   [ m01 ]
 *  [  .  ]      [        .         ]   [ m02 ]
 *  [  .  ]      [        .         ] * [ m10 ]
 *  [ x'<sub>n</sub> ]   =  [ x<sub>n</sub> y<sub>n</sub> 1  0  0  0 ]   [ m11 ]
 *  [ y'<sub>1</sub> ]      [ 0  0  0  x<sub>1</sub> y<sub>1</sub> 1 ]   [ m12 ]
 *  [ y'<sub>2</sub> ]      [ 0  0  0  x<sub>2</sub> y<sub>2</sub> 1 ]  
 *  [  .  ]      [        .         ]                               
 *  [  .  ]      [        .         ]                        
 *  [ y'<sub>n</sub> ]      [ 0  0  0  x<sub>n</sub> y<sub>n</sub> 1 ]   
 *  x' = A*m </pre>Using the least square method we get this result:
 * <pre><blockquote>
 *  m = (A<sup>T</sup>A)<sup>-1</sup> A<sup>T</sup>x'  </blockquote> </pre>
 *
 * @since 2.4
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/builder/AffineTransformBuilder.java $
 * @version $Id: AffineTransformBuilder.java 28982 2008-01-28 16:27:33Z acuster $
 * @author Jan Jezek
 */
public class AffineTransformBuilder extends ProjectiveTransformBuilder {
    protected AffineTransformBuilder() {
    }

/**
     * Creates AffineTransformBuilder for the set of properties. 
     * 
     * @param vectors list of {@linkplain
     * MappedPosition MappedPosition}
     */
    public AffineTransformBuilder(List vectors)
        throws MismatchedSizeException, MismatchedDimensionException,
            MismatchedReferenceSystemException {
        super.setMappedPositions(vectors);
    }

    /**
     * Returns the minimum number of points required by this builder,
     * which is 3.
     *
     * @return the minimum number of points required by this builder, which is
     *         3.
     */
    public int getMinimumPointCount() {
        return 3;
    }

   

    /**
     * Returns the matrix for Projective transformation setup as
     * Affine. The M matrix looks like this:
     * <pre>                                                       
     * [  m00  m01  m02  ]                           
     * [  m10  m11  m12  ]                              
     * [   0    0    1   ]                                                                   
     * </pre>
     *
     * @return Matrix M.
     */
    protected GeneralMatrix getProjectiveMatrix() {
        GeneralMatrix M = new GeneralMatrix(3, 3);
        double[] param = calculateLSM();
        double[] m0 = { param[0], param[1], param[2] };
        double[] m1 = { param[3], param[4], param[5] };
        double[] m2 = { 0, 0, 1 };
        M.setRow(0, m0);
        M.setRow(1, m1);
        M.setRow(2, m2);

        return M;
    }

    protected void fillAMatrix() {
        
        super.A = new GeneralMatrix(2 * getSourcePoints().length, 6);

        int numRow = getSourcePoints().length*2;

        // Creates X matrix
        for (int j = 0; j < (numRow / 2); j++) {
            A.setRow(j,
                new double[] {
        	    getSourcePoints()[j].getCoordinates()[0],
        	    getSourcePoints()[j].getCoordinates()[1], 1, 0, 0, 0
                });          
        }

        for (int j = numRow / 2; j < numRow; j++) {
            A.setRow(j,
                    new double[] {0, 0, 0,
        	    getSourcePoints()[j - (numRow / 2)].getCoordinates()[0],
        	    getSourcePoints()[j - (numRow / 2)].getCoordinates()[1], 1
                    });      
         
        }      
    }
}
