Java Code Examples for org.opengis.referencing.operation.Matrix#getElement()

The following examples show how to use org.opengis.referencing.operation.Matrix#getElement() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: Mercator.java    From sis with Apache License 2.0 6 votes vote down vote up
/**
 * Invoked when {@link #tryConcatenate(boolean, MathTransform, MathTransformFactory)} detected a
 * (inverse) → (affine) → (this) transforms sequence.
 */
@Override
final MathTransform tryConcatenate(boolean projectedSpace, Matrix affine, MathTransformFactory factory)
        throws FactoryException
{
    /*
     * Verify that the latitude row is an identity conversion except for the sign which is allowed to change
     * (but no scale and no translation are allowed).  Ignore the longitude row because it just pass through
     * this Mercator projection with no impact on any calculation.
     */
    if (affine.getElement(1,0) == 0 && affine.getElement(1, DIMENSION) == 0 && Math.abs(affine.getElement(1,1)) == 1) {
        if (factory != null) {
            return factory.createAffineTransform(affine);
        } else {
            return MathTransforms.linear(affine);
        }
    }
    return super.tryConcatenate(projectedSpace, affine, factory);
}
 
Example 2
Source File: GeoapiAssert.java    From sis with Apache License 2.0 6 votes vote down vote up
/**
 * Asserts that the given matrix is equals to the expected one, up to the given tolerance value.
 *
 * @param message   Header of the exception message in case of failure, or {@code null} if none.
 * @param expected  The expected matrix, which may be {@code null}.
 * @param actual    The matrix to compare, or {@code null}.
 * @param tolerance The tolerance threshold.
 */
public static void assertMatrixEquals(final String message, final Matrix expected, final Matrix actual, final double tolerance) {
    if (isNull(message, expected, actual)) {
        return;
    }
    final int numRow = actual.getNumRow();
    final int numCol = actual.getNumCol();
    assertEquals("numRow", expected.getNumRow(), numRow);
    assertEquals("numCol", expected.getNumCol(), numCol);
    for (int j=0; j<numRow; j++) {
        for (int i=0; i<numCol; i++) {
            final double e = expected.getElement(j,i);
            final double a = actual.getElement(j,i);
            if (!(StrictMath.abs(e - a) <= tolerance) && Double.doubleToLongBits(a) != Double.doubleToLongBits(e)) {
                fail("Matrix.getElement(" + j + ", " + i + "): expected " + e + " but got " + a);
            }
        }
    }
}
 
Example 3
Source File: DatumShiftTransform.java    From sis with Apache License 2.0 6 votes vote down vote up
/**
 * Computes the conversion factors needed for calls to {@link DatumShiftGrid#interpolateInCell(double, double, double[])}.
 * This method takes only the {@value DatumShiftGrid#INTERPOLATED_DIMENSIONS} first dimensions. If a conversion factor can
 * not be computed, then it is set to NaN.
 */
@SuppressWarnings("fallthrough")
private void computeConversionFactors() {
    scaleX = Double.NaN;
    scaleY = Double.NaN;
    x0     = Double.NaN;
    y0     = Double.NaN;
    if (grid != null) {
        final LinearTransform coordinateToGrid = grid.getCoordinateToGrid();
        final double toStandardUnit = Units.toStandardUnit(grid.getCoordinateUnit());
        if (!Double.isNaN(toStandardUnit)) {
            final Matrix m = coordinateToGrid.getMatrix();
            if (Matrices.isAffine(m)) {
                final int n = m.getNumCol() - 1;
                switch (m.getNumRow()) {
                    default: y0 = m.getElement(1,n); scaleY = diagonal(m, 1, n) / toStandardUnit;   // Fall through
                    case 1:  x0 = m.getElement(0,n); scaleX = diagonal(m, 0, n) / toStandardUnit;
                    case 0:  break;
                }
            }
        }
    }
}
 
Example 4
Source File: EllipsoidToCentricTransform.java    From sis with Apache License 2.0 6 votes vote down vote up
/**
 * If this transform returns three-dimensional outputs, and if the transform just after this one
 * just drops the height values, then replaces this transform by a two-dimensional one.
 * The intent is to handle the following sequence of operations defined in the EPSG database:
 *
 * <ol>
 *   <li>Inverse of <cite>Geographic/geocentric conversions</cite> (EPSG:9602)</li>
 *   <li><cite>Geographic 3D to 2D conversion</cite> (EPSG:9659)</li>
 * </ol>
 *
 * Replacing the above sequence by a two-dimensional {@code EllipsoidToCentricTransform} instance
 * allow the following optimizations:
 *
 * <ul>
 *   <li>Avoid computation of <var>h</var> value.</li>
 *   <li>Allow use of the more efficient {@link java.awt.geom.AffineTransform} after this transform
 *       instead than a transform based on a matrix of size 3×4.</li>
 * </ul>
 */
@Override
protected MathTransform tryConcatenate(final boolean applyOtherFirst, final MathTransform other,
        final MathTransformFactory factory) throws FactoryException
{
    if (!applyOtherFirst && forward.withHeight && other instanceof LinearTransform && other.getTargetDimensions() == 2) {
        /*
         * Found a 3×4 matrix after this transform. We can reduce to a 3×3 matrix only if no dimension
         * use the column that we are about to drop (i.e. all coefficients in that column are zero).
         */
        Matrix matrix = ((LinearTransform) other).getMatrix();
        if (matrix.getElement(0,2) == 0 &&
            matrix.getElement(1,2) == 0 &&
            matrix.getElement(2,2) == 0)
        {
            matrix = MatrixSIS.castOrCopy(matrix).removeColumns(2, 3);
            final MathTransform tr2D = forward.create2D().inverse();
            if (factory != null) {
                return factory.createConcatenatedTransform(tr2D, factory.createAffineTransform(matrix));
            } else {
                return ConcatenatedTransform.create(tr2D, MathTransforms.linear(matrix), factory);
            }
        }
    }
    return super.tryConcatenate(applyOtherFirst, other, factory);
}
 
Example 5
Source File: GeodeticCalculator.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Implementation of {@link #evaluateAt(double)} using the current φ₂, λ₂ and ∂φ₂/∂λ₂ values.
 * This method stores the projected coordinates in the {@link #point} array and stores the
 * derivative ∂y/∂x in the {@link #dx}, {@link #dy} fields.
 */
final void evaluateAtEndPoint() throws TransformException {
    if ((λ2 - λ1) * msinα1 < 0) {            // Reminder: Δλ or sin(α₁) may be zero.
        λ2 += 2*PI * signum(msinα1);         // We need λ₁ < λ₂ if heading east, or λ₁ > λ₂ if heading west.
    }
    final Matrix d = geographic(φ2, λ2).inverseTransform(point);    // `point` coordinates in user-specified CRS.
    final double dφ_dy = dφ_dy(φ2);                                 // ∂φ/∂y = cos(φ) for Mercator on a sphere of radius 1.
    final double m00 = d.getElement(0,0);
    final double m01 = d.getElement(0,1);
    final double m10 = d.getElement(1,0);
    final double m11 = d.getElement(1,1);
    double t = tolerance / dφ_dy;                                   // Tolerance for λ (second coordinate).
    εx = m00*tolerance + m01*t;                                     // Tolerance for x in user CRS.
    εy = m10*tolerance + m11*t;                                     // Tolerance for y in user CRS.
    /*
     * Return the tangent of the ending azimuth converted to the user CRS space. Note that if we draw the shape on
     * screen with (longitude, latitude) axes, the angles seen on screen are not the real angles measured on Earth.
     * In order to see the "real" angles, we need to draw the shape on a conformal projection such as Mercator.
     * Said otherwise, the angle value computed from the (dx,dy) vector is "real" only in a conformal projection.
     * Consequently if the output CRS is a Mercator projection, then the angle computed from the (dx,dy) vector
     * at the end of this method should be the ending azimuth angle unchanged. We achieve this equivalence by
     * multiplying mcosα2 by a factor which will cancel the ∂y/∂φ factor of Mercator projection at that latitude.
     * Note that there is no need to scale msinα2 since ∂x/∂λ = 1 everywhere on Mercator projection with a=1.
     */
    t  = mcosα2 * dφ_dy;
    dx = m00*t + m01*msinα2;                                        // Reminder: coordinates in (φ,λ) order.
    dy = m10*t + m11*msinα2;
}
 
Example 6
Source File: Matrices.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Compares the given matrices for equality, using the given relative or absolute tolerance threshold.
 * The matrix elements are compared as below:
 *
 * <ul>
 *   <li>{@link Double#NaN} values are considered equals to all other NaN values</li>
 *   <li>Infinite values are considered equal to other infinite values of the same sign</li>
 *   <li>All other values are considered equal if the absolute value of their difference is
 *       smaller than or equals to the threshold described below.</li>
 * </ul>
 *
 * If {@code relative} is {@code true}, then for any pair of values <var>v1</var><sub>j,i</sub>
 * and <var>v2</var><sub>j,i</sub> to compare, the tolerance threshold is scaled by
 * {@code max(abs(v1), abs(v2))}. Otherwise the threshold is used as-is.
 *
 * @param  m1        the first matrix to compare, or {@code null}.
 * @param  m2        the second matrix to compare, or {@code null}.
 * @param  epsilon   the tolerance value.
 * @param  relative  if {@code true}, then the tolerance value is relative to the magnitude
 *                   of the matrix elements being compared.
 * @return {@code true} if the values of the two matrix do not differ by a quantity greater
 *         than the given tolerance threshold.
 *
 * @see MatrixSIS#equals(Matrix, double)
 */
public static boolean equals(final Matrix m1, final Matrix m2, final double epsilon, final boolean relative) {
    if (m1 != m2) {
        if (m1 == null || m2 == null) {
            return false;
        }
        final int numRow = m1.getNumRow();
        if (numRow != m2.getNumRow()) {
            return false;
        }
        final int numCol = m1.getNumCol();
        if (numCol != m2.getNumCol()) {
            return false;
        }
        for (int j=0; j<numRow; j++) {
            for (int i=0; i<numCol; i++) {
                final double v1 = m1.getElement(j, i);
                final double v2 = m2.getElement(j, i);
                double tolerance = epsilon;
                if (relative) {
                    tolerance *= Math.max(Math.abs(v1), Math.abs(v2));
                }
                if (!(Math.abs(v1 - v2) <= tolerance)) {
                    if (Numerics.equals(v1, v2)) {
                        // Special case for NaN and infinite values.
                        continue;
                    }
                    return false;
                }
            }
        }
    }
    return true;
}
 
Example 7
Source File: MatrixSIS.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Copies the elements of the given matrix in the given array.
 * This method ignores the error terms, if any.
 *
 * @param  matrix    the matrix to copy.
 * @param  numRow    {@code matrix.getNumRow()}.
 * @param  numCol    {@code matrix.getNumCol()}.
 * @param  elements  where to copy the elements.
 */
static void getElements(final Matrix matrix, final int numRow, final int numCol, final double[] elements) {
    if (matrix instanceof MatrixSIS) {
        ((MatrixSIS) matrix).getElements(elements);
    } else {
        for (int k=0,j=0; j<numRow; j++) {
            for (int i=0; i<numCol; i++) {
                elements[k++] = matrix.getElement(j, i);
            }
        }
    }
}
 
Example 8
Source File: MathTransforms.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Creates an arbitrary linear transform from the specified matrix. Usually the matrix
 * {@linkplain org.apache.sis.referencing.operation.matrix.MatrixSIS#isAffine() is affine},
 * but this is not mandatory. Non-affine matrix will define a projective transform.
 *
 * <p>If the transform input dimension is {@code M}, and output dimension is {@code N},
 * then the given matrix shall have size {@code [N+1][M+1]}.
 * The +1 in the matrix dimensions allows the matrix to do a shift, as well as a rotation.
 * The {@code [M][j]} element of the matrix will be the <var>j</var>'th coordinate of the moved origin.</p>
 *
 * @param  matrix  the matrix used to define the linear transform.
 * @return the linear (usually affine) transform.
 *
 * @see #getMatrix(MathTransform)
 * @see DefaultMathTransformFactory#createAffineTransform(Matrix)
 */
public static LinearTransform linear(final Matrix matrix) {
    ArgumentChecks.ensureNonNull("matrix", matrix);
    final int sourceDimension = matrix.getNumCol() - 1;
    final int targetDimension = matrix.getNumRow() - 1;
    if (sourceDimension == targetDimension) {
        if (matrix.isIdentity()) {
            return identity(sourceDimension);
        }
        if (Matrices.isAffine(matrix)) {
            switch (sourceDimension) {
                case 1: {
                    return linear(matrix.getElement(0,0), matrix.getElement(0,1));
                }
                case 2: {
                    if (matrix instanceof ExtendedPrecisionMatrix) {
                        return new AffineTransform2D(((ExtendedPrecisionMatrix) matrix).getExtendedElements());
                    } else {
                        return new AffineTransform2D(
                                matrix.getElement(0,0), matrix.getElement(1,0),
                                matrix.getElement(0,1), matrix.getElement(1,1),
                                matrix.getElement(0,2), matrix.getElement(1,2));
                    }
                }
            }
        } else if (sourceDimension == 2) {
            return new ProjectiveTransform2D(matrix);
        }
    }
    final LinearTransform candidate = CopyTransform.create(matrix);
    if (candidate != null) {
        return candidate;
    }
    return new ProjectiveTransform(matrix).optimize();
}
 
Example 9
Source File: GridDerivation.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Applies a subsampling on the grid geometry to build.
 * This method can be invoked as an alternative to {@code subgrid(…)} methods if only the resolution needs to be changed.
 * The {@linkplain GridGeometry#getExtent() extent} of the {@linkplain #build() built} grid geometry will be derived
 * from {@link #getIntersection()} as below for each dimension <var>i</var>:
 *
 * <ul>
 *   <li>The {@linkplain GridExtent#getLow(int)  low}  is divided by {@code subsamplings[i]}, rounded toward zero.</li>
 *   <li>The {@linkplain GridExtent#getSize(int) size} is divided by {@code subsamplings[i]}, rounded toward zero.</li>
 *   <li>The {@linkplain GridExtent#getHigh(int) high} is recomputed from above low and size.</li>
 * </ul>
 *
 * The {@linkplain GridGeometry#getGridToCRS(PixelInCell) grid to CRS} transform is scaled accordingly
 * in order to map approximately to the same {@linkplain GridGeometry#getEnvelope() envelope}.
 *
 * @param  subsamplings  the subsampling to apply on each grid dimension. All values shall be greater than zero.
 *         If the array length is shorter than the number of dimensions, missing values are assumed to be 1.
 * @return {@code this} for method call chaining.
 * @throws IllegalStateException if a subsampling has already been set,
 *         for example by a call to {@link #subgrid(Envelope, double...) subgrid(…)}.
 *
 * @see #subgrid(GridGeometry)
 * @see #getSubsamplings()
 * @see GridExtent#subsample(int...)
 */
public GridDerivation subsample(final int... subsamplings) {
    ArgumentChecks.ensureNonNull("subsamplings", subsamplings);
    if (toBase != null) {
        throw new IllegalStateException(Errors.format(Errors.Keys.ValueAlreadyDefined_1, "subsamplings"));
    }
    // Validity of the subsamplings values will be verified by GridExtent.subsample(…) invoked below.
    final GridExtent extent = (baseExtent != null) ? baseExtent : base.getExtent();
    Matrix affine = null;
    final int dimension = extent.getDimension();
    for (int i = Math.min(dimension, subsamplings.length); --i >= 0;) {
        final int s = subsamplings[i];
        if (s != 1) {
            if (affine == null) {
                affine = Matrices.createIdentity(dimension + 1);
                scaledExtent = extent.subsample(subsamplings);
            }
            final double sd = s;
            affine.setElement(i, i, sd);
            affine.setElement(i, dimension, extent.getLow(i) - scaledExtent.getLow(i) * sd);
        }
    }
    if (affine != null) {
        toBase = MathTransforms.linear(affine);
        /*
         * Take the matrix scale factors as the resolutions, unless the scale factors were already computed
         * by subgrid(GridGeometry). In the later case the scales may have fractional values, which we keep.
         */
        if (scales == null) {
            scales = new double[dimension];
            for (int i=0; i<dimension; i++) {
                scales[i] = affine.getElement(i,i);
            }
        }
    }
    return this;
}
 
Example 10
Source File: TransferFunction.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Sets the {@link #scale} and {@link #offset} terms from the given function.
 *
 * @param  function  the transform to set.
 * @throws IllegalArgumentException if this method does not recognize the given transform.
 */
private void setLinearTerms(final LinearTransform function) throws IllegalArgumentException {
    final Matrix m = function.getMatrix();
    final int numRow = m.getNumRow();
    final int numCol = m.getNumCol();
    if (numRow != 2 || numCol != 2) {
        final Integer two = 2;
        throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedMatrixSize_4, two, two, numRow, numCol));
    }
    scale  = m.getElement(0, 0);
    offset = m.getElement(0, 1);
}
 
Example 11
Source File: DatumShiftTransform.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Returns the value on the diagonal of the given matrix, provided that all other non-translation terms are 0.
 *
 * @param  m  the matrix from which to get the scale factor on a row.
 * @param  j  the row for which to get the scale factor.
 * @param  n  index of the last column.
 * @return the scale factor on the diagonal, or NaN.
 */
private static double diagonal(final Matrix m, final int j, int n) {
    while (--n >= 0) {
        if (j != n && m.getElement(j, n) != 0) {
            return Double.NaN;
        }
    }
    return m.getElement(j, j);
}
 
Example 12
Source File: CopyTransform.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * If a transform can be created from the given matrix, returns it.
 * Otherwise returns {@code null}.
 */
static CopyTransform create(final Matrix matrix) {
    final int srcDim = matrix.getNumCol() - 1;
    final int dstDim = matrix.getNumRow() - 1;
    for (int i=0; i <= srcDim; i++) {
        if (matrix.getElement(dstDim, i) != (i == srcDim ? 1 : 0)) {
            // Not an affine transform (ignoring if square or not).
            return null;
        }
    }
    final int[] indices = new int[dstDim];
    for (int j=0; j<dstDim; j++) {
        if (matrix.getElement(j, srcDim) != 0) {
            // The matrix has translation terms.
            return null;
        }
        boolean found = false;
        for (int i=0; i<srcDim; i++) {
            final double elt = matrix.getElement(j, i);
            if (elt != 0) {
                if (elt != 1 || found) {
                    // Not a simple copy operation.
                    return null;
                }
                indices[j] = i;
                found = true;
            }
        }
        if (!found) {
            // Target coordinate unconditionally set to 0 (not a copy).
            return null;
        }
    }
    return new CopyTransform(srcDim, indices);
}
 
Example 13
Source File: TensorValues.java    From sis with Apache License 2.0 5 votes vote down vote up
/**
 * Sets all parameter values to the element value in the specified matrix.
 * After this method call, {@link #values} will returns only the elements
 * different from the default value.
 *
 * @param  matrix  the matrix to copy in this group of parameters.
 */
final void setMatrix(final Matrix matrix) {
    final int numRow = matrix.getNumRow();
    final int numCol = matrix.getNumCol();
    dimensions[0].setValue(numRow);
    dimensions[1].setValue(numCol);
    values = null;
    final int[] indices = new int[2];
    for (int j=0; j<numRow; j++) {
        indices[0] = j;
        ParameterValue<?>[] row = null;
        for (int i=0; i<numCol; i++) {
            indices[1] = i;
            ParameterDescriptor<E> descriptor = descriptors.getElementDescriptor(indices);
            final E def = descriptor.getDefaultValue();
            final double element = matrix.getElement(j,i);
            if (!(def instanceof Number) || !Numerics.equalsIgnoreZeroSign(element, ((Number) def).doubleValue())) {
                final ParameterValue<?> value = descriptor.createValue();
                value.setValue(element);
                if (row == null) {
                    row = new ParameterValue<?>[numCol];
                    if (values == null) {
                        values = new ParameterValue<?>[numRow][];
                    }
                    values[j] = row;
                }
                row[i] = value;
            }
        }
    }
}
 
Example 14
Source File: PassThroughTransform.java    From sis with Apache License 2.0 4 votes vote down vote up
/**
 * If the given matrix to be concatenated to this transform, can be concatenated to the
 * sub-transform instead, returns the matrix to be concatenated to the sub-transform.
 * Otherwise returns {@code null}.
 *
 * <p>This method does not verify if the matrix size is compatible with this transform dimension.</p>
 *
 * @param  applyOtherFirst  {@code true} if the transformation order is {@code matrix} followed by {@code this}, or
 *                          {@code false} if the transformation order is {@code this} followed by {@code matrix}.
 */
private Matrix toSubMatrix(final boolean applyOtherFirst, final Matrix matrix) {
    final int numRow = matrix.getNumRow();
    final int numCol = matrix.getNumCol();
    if (numRow != numCol) {
        // Current implementation requires a square matrix.
        return null;
    }
    final int subDim = applyOtherFirst ? subTransform.getSourceDimensions()
                                       : subTransform.getTargetDimensions();
    final MatrixSIS sub = Matrices.createIdentity(subDim + 1);
    /*
     * Ensure that every dimensions which are scaled by the affine transform are one
     * of the dimensions modified by the sub-transform, and not any other dimension.
     */
    for (int j=numRow; --j>=0;) {
        final int sj = j - firstAffectedCoordinate;
        for (int i=numCol; --i>=0;) {
            final double element = matrix.getElement(j, i);
            if (sj >= 0 && sj < subDim) {
                final int si;
                final boolean copy;
                if (i == numCol-1) {                    // Translation term (last column)
                    si = subDim;
                    copy = true;
                } else {                                // Any term other than translation.
                    si = i - firstAffectedCoordinate;
                    copy = (si >= 0 && si < subDim);
                }
                if (copy) {
                    sub.setElement(sj, si, element);
                    continue;
                }
            }
            if (element != (i == j ? 1 : 0)) {
                // Found a dimension which perform some scaling or translation.
                return null;
            }
        }
    }
    return sub;
}
 
Example 15
Source File: TransformSeparator.java    From sis with Apache License 2.0 4 votes vote down vote up
/**
 * Removes the sources dimensions that are not required for computing the target dimensions.
 * This method is invoked only if {@link #sourceDimensions} is non-null at {@link #separate()} invocation time.
 * This method can operate only on the first transform of a transformation chain.
 * If this method succeed, then {@link #sourceDimensions} will be updated.
 *
 * <p>This method can process only linear transforms (potentially indirectly through a concatenated transform).
 * Actually it would be possible to also process pass-through transform followed by a linear transform, but this
 * case should have been optimized during transform concatenation. If it is not the case, consider improving the
 * {@link PassThroughTransform#tryConcatenate(boolean, MathTransform, MathTransformFactory)} method instead then
 * this one.</p>
 *
 * @param  head  the first transform of a transformation chain.
 * @return the reduced transform, or {@code head} if this method did not reduced the transform.
 */
private MathTransform removeUnusedSourceDimensions(final MathTransform head) {
    Matrix m = MathTransforms.getMatrix(head);
    if (m != null) {
        int[] retainedDimensions = ArraysExt.EMPTY_INT;
        final int dimension = m.getNumCol() - 1;            // Number of source dimensions (ignore translations column).
        final int numRows   = m.getNumRow();                // Number of target dimensions + 1.
        for (int i=0; i<dimension; i++) {
            for (int j=0; j<numRows; j++) {
                if (m.getElement(j,i) != 0) {
                    // Found a source dimension which is required by target dimension.
                    final int length = retainedDimensions.length;
                    retainedDimensions = Arrays.copyOf(retainedDimensions, length+1);
                    retainedDimensions[length] = i;
                    break;
                }
            }
        }
        if (retainedDimensions.length != dimension) {
            /*
             * If we do not retain all dimensions, remove the matrix columns corresponding to the excluded
             * source dimensions and create a new transform. We remove consecutive columns in single calls
             * to 'removeColumns', from 'lower' inclusive to 'upper' exclusive.
             */
            int upper = dimension;
            for (int i = retainedDimensions.length; --i >= -1;) {
                final int keep = (i >= 0) ? retainedDimensions[i] : -1;
                final int lower = keep + 1;                                     // First column to exclude.
                if (lower != upper) {
                    // Remove source dimensions that are not retained.
                    m = MatrixSIS.castOrCopy(m).removeColumns(lower, upper);
                }
                upper = keep;
            }
            /*
             * If the user specified source dimensions, the indices need to be adjusted.
             * This loop has no effect if all source dimensions were kept before this method call.
             */
            for (int i=0; i<retainedDimensions.length; i++) {
                retainedDimensions[i] = sourceDimensions[retainedDimensions[i]];
            }
            sourceDimensions = retainedDimensions;
            return MathTransforms.linear(m);
        }
    } else if (head instanceof ConcatenatedTransform) {
        final MathTransform transform1 = ((ConcatenatedTransform) head).transform1;
        final MathTransform reduced = removeUnusedSourceDimensions(transform1);
        if (reduced != transform1) {
            return MathTransforms.concatenate(reduced, ((ConcatenatedTransform) head).transform2);
        }
    }
    return head;
}
 
Example 16
Source File: BursaWolfParameters.java    From sis with Apache License 2.0 4 votes vote down vote up
/**
 * Sets all Bursa-Wolf parameters from the given <cite>Position Vector transformation</cite> matrix.
 * The matrix shall comply to the following constraints:
 *
 * <ul>
 *   <li>The matrix shall be {@linkplain org.apache.sis.referencing.operation.matrix.MatrixSIS#isAffine() affine}.</li>
 *   <li>The sub-matrix defined by {@code matrix} without the last row and last column shall be
 *       <a href="http://en.wikipedia.org/wiki/Skew-symmetric_matrix">skew-symmetric</a> (a.k.a. antisymmetric).</li>
 * </ul>
 *
 * @param  matrix     the matrix from which to get Bursa-Wolf parameters.
 * @param  tolerance  the tolerance error for the skew-symmetric matrix test, in units of PPM or arc-seconds (e.g. 1E-8).
 * @throws IllegalArgumentException if the specified matrix does not met the conditions.
 *
 * @see #getPositionVectorTransformation(Date)
 */
public void setPositionVectorTransformation(final Matrix matrix, final double tolerance) throws IllegalArgumentException {
    final int numRow = matrix.getNumRow();
    final int numCol = matrix.getNumCol();
    if (numRow != SIZE || numCol != SIZE) {
        final Integer n = SIZE;
        throw new IllegalArgumentException(Errors.format(Errors.Keys.MismatchedMatrixSize_4, n, n, numRow, numCol));
    }
    if (!Matrices.isAffine(matrix)) {
        throw new IllegalArgumentException(Resources.format(Resources.Keys.NotAnAffineTransform));
    }
    /*
     * Translation terms, taken "as-is".
     * If the matrix contains only translation terms (which is often the case), we are done.
     */
    tX = matrix.getElement(0,3);
    tY = matrix.getElement(1,3);
    tZ = matrix.getElement(2,3);
    if (Matrices.isTranslation(matrix)) {                   // Optimization for a common case.
        return;
    }
    /*
     * Scale factor: take the average of elements on the diagonal. All those
     * elements should have the same value, but we tolerate slight deviation
     * (this will be verified later).
     */
    final DoubleDouble S = DoubleDouble.castOrCopy(getNumber(matrix, 0,0));
    S.addGuessError(getNumber(matrix, 1,1));
    S.addGuessError(getNumber(matrix, 2,2));
    S.divide(3);
    /*
     * Computes: RS = S * toRadians(1″)
     *           dS = (S-1) * PPM
     */
    final DoubleDouble RS = DoubleDouble.createSecondsToRadians();
    RS.multiply(S);
    S.add(-1);
    S.multiply(PPM);
    dS = S.doubleValue();
    /*
     * Rotation terms. Each rotation terms appear twice, with one value being the negative of the other value.
     * We verify this skew symmetric aspect in the loop. We also opportunistically verify that the scale terms
     * are uniform.
     */
    for (int j=0; j < SIZE-1; j++) {
        if (!(abs((matrix.getElement(j,j) - 1)*PPM - dS) <= tolerance)) {
            throw new IllegalArgumentException(Resources.format(Resources.Keys.NonUniformScale));
        }
        for (int i = j+1; i < SIZE-1; i++) {
            S.setFrom(RS);
            S.inverseDivideGuessError(getNumber(matrix, j,i));          // Negative rotation term.
            double value = S.value;
            double error = S.error;
            S.setFrom(RS);
            S.inverseDivideGuessError(getNumber(matrix, i,j));          // Positive rotation term.
            if (!(abs(value + S.value) <= tolerance)) {                 // We expect r1 ≈ -r2
                throw new IllegalArgumentException(Resources.format(Resources.Keys.NotASkewSymmetricMatrix));
            }
            S.subtract(value, error);
            S.multiply(0.5);
            value = S.doubleValue();                                    // Average of the two rotation terms.
            switch (j*SIZE + i) {
                case 1: rZ =  value; break;
                case 2: rY = -value; break;
                case 6: rX =  value; break;
            }
        }
    }
}
 
Example 17
Source File: ResidualGrid.java    From sis with Apache License 2.0 4 votes vote down vote up
/** Creates a new matrix for the specified dimension. */
Data(final int dim, final Matrix denormalization) {
    c0 = denormalization.getElement(dim, 0);
    c1 = denormalization.getElement(dim, 1);
    c2 = denormalization.getElement(dim, 2);
}
 
Example 18
Source File: VerticalOffset.java    From sis with Apache License 2.0 3 votes vote down vote up
/**
 * Invoked by {@link org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory} after
 * the transform has been created but before it is concatenated with operations performing axis changes.
 * This method performs the parameter sign adjustment as documented in the class javadoc if and only if
 * this method detects that the target axis is oriented toward down. This orientation is detected by a
 * negative sign for the <var>m₀₀</var> coefficient in the given 2×2 affine transform matrix.
 *
 * <div class="note"><b>Implementation note:</b>
 * for now we define this method as a static one because it is the only special case handled by
 * {@code DefaultMathTransformFactory}. But if there is more special cases in a future SIS version,
 * then we should make this method non-static and declare an overrideable {@code postCreate} method
 * in {@link AbstractProvider} instead.</div>
 *
 * @param  parameterized  the transform created by {@code createMathTransform(…)}.
 * @param  after  the matrix for the operation to be concatenated after {@code parameterized}.
 * @return the transform to use instead of {@code parameterized}.
 * @throws FactoryException if an error occurred while creating the new transform.
 */
public static MathTransform postCreate(MathTransform parameterized, final Matrix after) throws FactoryException {
    if (after.getElement(0,0) < 0) try {
        parameterized = parameterized.inverse();
    } catch (NoninvertibleTransformException e) {
        throw new FactoryException(e);                  // Should never happe since matrix element is not zero.
    }
    return parameterized;
}
 
Example 19
Source File: BursaWolfParameters.java    From sis with Apache License 2.0 3 votes vote down vote up
/**
 * Retrieves the value at the specified row and column of the given matrix, wrapped in a {@code Number}.
 * The {@code Number} type depends on the matrix accuracy.
 *
 * @param  matrix  the matrix from which to get the number.
 * @param  row     the row index, from 0 inclusive to {@link Matrix#getNumRow()} exclusive.
 * @param  column  the column index, from 0 inclusive to {@link Matrix#getNumCol()} exclusive.
 * @return the current value at the given row and column.
 */
private static Number getNumber(final Matrix matrix, final int row, final int column) {
    if (matrix instanceof MatrixSIS) {
        return ((MatrixSIS) matrix).getNumber(row, column);
    } else {
        return matrix.getElement(row, column);
    }
}
 
Example 20
Source File: Matrix1.java    From sis with Apache License 2.0 2 votes vote down vote up
/**
 * Creates a new matrix initialized to the same value than the specified one.
 * The specified matrix size must be {@value #SIZE}×{@value #SIZE}.
 * This is not verified by this constructor, since it shall be verified by {@link Matrices}.
 *
 * @param  matrix  the matrix to copy.
 */
Matrix1(final Matrix matrix) {
    m00 = matrix.getElement(0,0);
}