Search in sources :

Example 46 with Matrix

use of org.opengis.referencing.operation.Matrix in project sis by apache.

the class TensorValues method toMatrix.

/**
 * Creates a matrix from this group of parameters.
 * This operation is allowed only for tensors of {@linkplain TensorParameters#rank() rank} 2.
 *
 * @return a matrix created from this group of parameters.
 */
final Matrix toMatrix() {
    final int numRow = dimensions[0].intValue();
    final int numCol = dimensions[1].intValue();
    final Matrix matrix = Matrices.createDiagonal(numRow, numCol);
    if (values != null) {
        for (int j = 0; j < numRow; j++) {
            final ParameterValue<?>[] row = (ParameterValue<?>[]) values[j];
            if (row != null) {
                for (int i = 0; i < numCol; i++) {
                    final ParameterValue<?> element = row[i];
                    if (element != null) {
                        matrix.setElement(j, i, element.doubleValue());
                    }
                }
            }
        }
    }
    return matrix;
}
Also used : Matrix(org.opengis.referencing.operation.Matrix) ParameterValue(org.opengis.parameter.ParameterValue) GeneralParameterValue(org.opengis.parameter.GeneralParameterValue)

Example 47 with Matrix

use of org.opengis.referencing.operation.Matrix in project sis by apache.

the class AffineTest method testWKT.

/**
 * Tests WKT formatting, and in particular the adjustment according
 * whether we comply with EPSG:9624 definition or not.
 */
@Test
@DependsOnMethod("testParameters")
public void testWKT() {
    final Matrix matrix = Matrices.createDiagonal(3, 3);
    assertWktEquals("PARAMETERGROUP[“Affine parametric transformation”," + " ID[“EPSG”, 9624]]", Affine.parameters(matrix));
    /*
         * Try arbitrary values.
         */
    // A1
    matrix.setElement(0, 1, 2);
    // B1
    matrix.setElement(1, 1, 0);
    // B2
    matrix.setElement(1, 2, -1);
    assertWktEquals("PARAMETERGROUP[“Affine parametric transformation”,\n" + "  PARAMETER[“A1”, 2.0, ID[“EPSG”, 8624]],\n" + "  PARAMETER[“B1”, 0.0, ID[“EPSG”, 8640]],\n" + "  PARAMETER[“B2”, -1.0, ID[“EPSG”, 8641]],\n" + "  ID[“EPSG”, 9624]]", Affine.parameters(matrix));
    /*
         * Setting a value on the last row make the matrix non-affine.
         * So it should not be anymore EPSG:9624.
         */
    // C0
    matrix.setElement(2, 0, 3);
    assertWktEquals("PARAMETERGROUP[“Affine”,\n" + "  PARAMETER[“num_row”, 3],\n" + "  PARAMETER[“num_col”, 3],\n" + "  PARAMETER[“elt_0_1”, 2.0],\n" + "  PARAMETER[“elt_1_1”, 0.0],\n" + "  PARAMETER[“elt_1_2”, -1.0],\n" + "  PARAMETER[“elt_2_0”, 3.0]]", Affine.parameters(matrix));
}
Also used : Matrix(org.opengis.referencing.operation.Matrix) Test(org.junit.Test) DependsOnMethod(org.apache.sis.test.DependsOnMethod)

Example 48 with Matrix

use of org.opengis.referencing.operation.Matrix in project sis by apache.

the class TransformSeparator method filterSourceDimensions.

/**
 * Creates a transform for the same mathematic than the given {@code step}
 * but expecting only the given dimensions as inputs.
 * This method is invoked by {@link #separate()} when user-specified source dimensions need to be taken in account.
 * The given {@code step} and {@code dimensions} are typically the values of
 * {@link #transform} and {@link #sourceDimensions} fields respectively, but not necessarily.
 * In particular those arguments will differ when this method is invoked recursively for processing
 * concatenated or {@linkplain PassThroughTransform#getSubTransform() sub-transforms}.
 *
 * <p>Subclasses can override this method if they need to handle some {@code MathTransform} implementations
 * in a special way. However all implementations of this method shall obey to the following contract:</p>
 * <ul class="verbose">
 *   <li>{@link #sourceDimensions} and {@link #targetDimensions} should not be assumed accurate
 *       since they may be temporarily outdated or modified during recursive calls to this method.</li>
 *   <li>{@link #sourceDimensions} should not be modified by this method.</li>
 *   <li>{@link #targetDimensions} <strong>must</strong> be <em>overwritten</em> (not updated) by this method to the
 *       sequence of all target dimensions of {@code step} that are also target dimensions of the returned transform.
 *       The indices shall be in strictly increasing order from 0 inclusive to
 *       {@code step.getTargetDimensions()} exclusive.</li>
 * </ul>
 *
 * @param  step        the transform for which to retain only a subset of the source dimensions.
 * @param  dimensions  indices of the source dimensions of {@code step} to retain.
 * @return a transform expecting only the given source dimensions.
 * @throws FactoryException if the given transform is not separable.
 */
@SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
protected MathTransform filterSourceDimensions(final MathTransform step, final int[] dimensions) throws FactoryException {
    if (dimensions.length == 0) {
        return IdentityTransform.create(0);
    }
    final int numSrc = step.getSourceDimensions();
    final int numTgt = step.getTargetDimensions();
    final int lower = dimensions[0];
    final int upper = dimensions[dimensions.length - 1] + 1;
    if (lower == 0 && upper == numSrc && dimensions.length == numSrc) {
        targetDimensions = series(0, numTgt);
        return step;
    }
    if (step.isIdentity()) {
        targetDimensions = dimensions;
        return IdentityTransform.create(dimensions.length);
    }
    if (step instanceof ConcatenatedTransform) {
        final ConcatenatedTransform ctr = (ConcatenatedTransform) step;
        final MathTransform step1 = filterSourceDimensions(ctr.transform1, dimensions);
        final MathTransform step2 = filterSourceDimensions(ctr.transform2, targetDimensions);
        return factory.createConcatenatedTransform(step1, step2);
    // Keep the 'targetDimensions' computed by the last step.
    }
    /*
         * Special case for the passthrough transform: if at least one input dimension belong to the pass-
         * through sub-transform, then invoke this method recursively for the sub-transform dimensions.
         */
    if (step instanceof PassThroughTransform) {
        final PassThroughTransform passThrough = (PassThroughTransform) step;
        final int numSubSrc = passThrough.subTransform.getSourceDimensions();
        final int numNewDim = passThrough.subTransform.getTargetDimensions() - numSubSrc;
        final int subLower = passThrough.firstAffectedOrdinate;
        final int subUpper = subLower + numSubSrc;
        int[] subDimensions = new int[dimensions.length];
        targetDimensions = null;
        int n = 0;
        for (int dim : dimensions) {
            if (dim >= subLower) {
                if (dim < subUpper) {
                    // Dimension n belong to the subtransform.
                    subDimensions[n++] = dim - subLower;
                    continue;
                }
                dim += numNewDim;
            }
            /*
                 * Dimension n belong to heading or trailing dimensions.
                 * Passthrough, after adjustment for trailing dimensions.
                 */
            targetDimensions = insert(targetDimensions, dim);
        }
        subDimensions = ArraysExt.resize(subDimensions, n);
        /*
             * If no source dimension belong to the sub-transform, then all source dimensions are heading or
             * trailing dimensions. A passthrough transform without its sub-transform is an identity transform.
             */
        if (n == 0) {
            return IdentityTransform.create(dimensions.length);
        }
        /*
             * There is at least one dimension to separate in the sub-transform. Perform this separation and get
             * the list of target dimensions. We need to offset the target dimensions by the amount of leading
             * dimensions once the separation is done, in order to translate from the sub-transform's dimension
             * numbering to the transform's numbering.
             */
        int[] target = targetDimensions;
        final MathTransform subTransform = filterSourceDimensions(passThrough.subTransform, subDimensions);
        for (final int dim : targetDimensions) {
            target = insert(target, dim + subLower);
        }
        targetDimensions = target;
        /*
             * If all source dimensions not in the sub-transform are consecutive numbers, we can use our passthrough
             * transform implementation. The "consecutive numbers" requirement (expressed in the 'if' statement below)
             * is a consequence of a limitation in our current implementation: our current passthrough transform does
             * not accept arbitrary index for modified ordinates.
             */
        if (containsAll(dimensions, lower, subLower) && containsAll(dimensions, subUpper, upper)) {
            return factory.createPassThroughTransform(subLower - lower, subTransform, Math.max(0, upper - subUpper));
        }
    }
    /*
         * If the transform is affine (or at least projective), express the transform as a matrix. Then, select
         * target dimensions that depend only on specified source dimensions. If a target dimension depends on
         * at least one discarded source dimension, then that output dimension will be discarded as well.
         */
    final Matrix matrix = MathTransforms.getMatrix(step);
    if (matrix != null) {
        targetDimensions = null;
        int startOfRow = 0;
        boolean isLastRowAccepted = false;
        final int numFilteredColumns = (dimensions.length + 1);
        double[] elements = new double[(numTgt + 1) * numFilteredColumns];
        reduce: for (int j = 0; j <= numTgt; j++) {
            /*
                 * For each target dimension (i.e. a matrix row), find the matrix elements (excluding translation
                 * terms in the last column) for each source dimension to be kept. If a dependancy to at least one
                 * discarded input dimension is found, then the whole output dimension is discarded.
                 */
            int filteredColumn = 0;
            for (int i = 0; i < numSrc; i++) {
                final double element = matrix.getElement(j, i);
                if (filteredColumn < dimensions.length && dimensions[filteredColumn] == i) {
                    elements[startOfRow + filteredColumn++] = element;
                } else if (element != 0) {
                    /*
                         * Output dimension 'j' depends on one of discarded input dimension 'i'.
                         * The whole row will be discarded.
                         */
                    continue reduce;
                }
            }
            // Copy the translation term.
            elements[startOfRow + filteredColumn++] = matrix.getElement(j, numSrc);
            // We should have used all values in the 'dimensions' array.
            assert filteredColumn == numFilteredColumns : filteredColumn;
            startOfRow += numFilteredColumns;
            if (j == numTgt) {
                /*
                     * In an affine transform, the last row is usually [0 0 0 … 1].
                     * This is not a real dimension, but nevertheless mandatory.
                     */
                isLastRowAccepted = true;
            } else {
                targetDimensions = insert(targetDimensions, j);
            }
        }
        if (isLastRowAccepted) {
            elements = ArraysExt.resize(elements, startOfRow);
            return factory.createAffineTransform(Matrices.create(startOfRow / numFilteredColumns, numFilteredColumns, elements));
        }
    /*
             * In an affine transform, the last row is not supposed to have dependency to any source dimension.
             * But if we reach this point, our matrix has such dependencies.
             */
    }
    throw new FactoryException(Resources.format(Resources.Keys.NotAnAffineTransform));
}
Also used : Matrix(org.opengis.referencing.operation.Matrix) MathTransform(org.opengis.referencing.operation.MathTransform) FactoryException(org.opengis.util.FactoryException)

Example 49 with Matrix

use of org.opengis.referencing.operation.Matrix in project sis by apache.

the class DefaultConversionTest method createLongitudeRotation.

/**
 * Creates a very simple conversion performing a longitude rotation.
 * The source CRS shall use the Paris prime meridian and the target CRS the Greenwich prime meridian,
 * at least conceptually. See {@link #createLongitudeRotation(boolean)} for an explanation about why
 * this is not really a valid conversion.
 *
 * @param  sourceCRS         a CRS using the Paris prime meridian.
 * @param  targetCRS         a CRS using the Greenwich prime meridian.
 * @param  interpolationCRS  a dummy interpolation CRS, or {@code null} if none.
 */
private static DefaultConversion createLongitudeRotation(final GeographicCRS sourceCRS, final GeographicCRS targetCRS, final TemporalCRS interpolationCRS) {
    /*
         * The following code fills the parameter values AND creates itself the MathTransform instance
         * (indirectly, through the matrix). The later step is normally not our business, since we are
         * supposed to only fill the parameter values and let MathTransformFactory creates the transform
         * from the parameters. But we don't do the normal steps here because this class is a unit test:
         * we want to test DefaultConversion in isolation of MathTransformFactory.
         */
    final int interpDim = ReferencingUtilities.getDimension(interpolationCRS);
    final int sourceDim = sourceCRS.getCoordinateSystem().getDimension();
    final int targetDim = targetCRS.getCoordinateSystem().getDimension();
    final OperationMethod method = DefaultOperationMethodTest.create("Longitude rotation", "9601", "EPSG guidance note #7-2", sourceDim, DefaultParameterDescriptorTest.createEPSG("Longitude offset", (short) 8602));
    final ParameterValueGroup pg = method.getParameters().createValue();
    pg.parameter("Longitude offset").setValue(OFFSET);
    final Matrix rotation = Matrices.createDiagonal(// Number of rows.
    targetDim + interpDim + 1, // Number of columns.
    sourceDim + interpDim + 1);
    rotation.setElement(interpDim, interpDim + sourceDim, OFFSET);
    /*
         * In theory we should not need to provide the parameters explicitly to the constructor since
         * we are supposed to be able to find them from the MathTransform. But in this simple test we
         * did not bothered to define a specialized MathTransform class for our case. So we will help
         * a little bit DefaultConversion by telling it the parameters that we used.
         */
    final Map<String, Object> properties = new HashMap<>(4);
    properties.put(DefaultTransformation.NAME_KEY, "Paris to Greenwich");
    properties.put(ReferencingServices.PARAMETERS_KEY, pg);
    return new DefaultConversion(properties, sourceCRS, targetCRS, interpolationCRS, method, MathTransforms.linear(rotation));
}
Also used : Matrix(org.opengis.referencing.operation.Matrix) ParameterValueGroup(org.opengis.parameter.ParameterValueGroup) HashMap(java.util.HashMap) OperationMethod(org.opengis.referencing.operation.OperationMethod)

Example 50 with Matrix

use of org.opengis.referencing.operation.Matrix in project sis by apache.

the class LinearTransformBuilderTest method test2D.

/**
 * Implementation of {@link #testExact2D()} and {@link #testNonExact2D()}.
 *
 * @param  rd              the random number generator to use.
 * @param  numPts          the number of points to generate.
 * @param  addErrors       {@code true} for adding a random error in the target points.
 * @param  scaleTolerance  tolerance threshold for floating point comparisons.
 */
private static void test2D(final Random rd, final int numPts, final boolean addErrors, final double scaleTolerance, final double translationTolerance) throws FactoryException {
    /*
         * Create an AffineTransform to use as the reference implementation.
         */
    final AffineTransform ref = AffineTransform.getRotateInstance(// Rotation angle
    rd.nextDouble() * (2 * StrictMath.PI), // Center X
    rd.nextDouble() * 30 - 12, // Center Y
    rd.nextDouble() * 10 - 8);
    final Map<DirectPosition2D, DirectPosition2D> pos = new HashMap<>(numPts);
    for (int i = 0; i < numPts; i++) {
        final DirectPosition2D src = new DirectPosition2D(rd.nextDouble() * 100 - 50, rd.nextDouble() * 200 - 75);
        final DirectPosition2D tgt = new DirectPosition2D();
        assertSame(tgt, ref.transform(src, tgt));
        if (addErrors) {
            tgt.x += rd.nextDouble() * 10 - 5;
            tgt.y += rd.nextDouble() * 10 - 5;
        }
        assertNull(pos.put(src, tgt));
    }
    /*
         * Create the fitted transform to test.
         */
    final LinearTransformBuilder builder = new LinearTransformBuilder();
    builder.setControlPoints(pos);
    final Matrix m = builder.create(null).getMatrix();
    /*
         * Compare the coefficients with the reference implementation.
         */
    assertEquals("m₀₀", ref.getScaleX(), m.getElement(0, 0), scaleTolerance);
    assertEquals("m₀₁", ref.getShearX(), m.getElement(0, 1), scaleTolerance);
    assertEquals("m₀₂", ref.getTranslateX(), m.getElement(0, 2), translationTolerance);
    assertEquals("m₁₀", ref.getShearY(), m.getElement(1, 0), scaleTolerance);
    assertEquals("m₁₁", ref.getScaleY(), m.getElement(1, 1), scaleTolerance);
    assertEquals("m₁₂", ref.getTranslateY(), m.getElement(1, 2), translationTolerance);
    assertArrayEquals("correlation", new double[] { 1, 1 }, builder.correlation(), scaleTolerance);
}
Also used : Matrix(org.opengis.referencing.operation.Matrix) HashMap(java.util.HashMap) AffineTransform(java.awt.geom.AffineTransform) DirectPosition2D(org.apache.sis.geometry.DirectPosition2D)

Aggregations

Matrix (org.opengis.referencing.operation.Matrix)63 Test (org.junit.Test)20 DependsOnMethod (org.apache.sis.test.DependsOnMethod)11 MathTransform (org.opengis.referencing.operation.MathTransform)8 HashMap (java.util.HashMap)6 ParameterValueGroup (org.opengis.parameter.ParameterValueGroup)6 NoninvertibleTransformException (org.opengis.referencing.operation.NoninvertibleTransformException)5 TransformException (org.opengis.referencing.operation.TransformException)5 FormattableObject (org.apache.sis.io.wkt.FormattableObject)4 DirectPosition1D (org.apache.sis.geometry.DirectPosition1D)3 ExtendedPrecisionMatrix (org.apache.sis.internal.referencing.ExtendedPrecisionMatrix)3 Matrix3 (org.apache.sis.referencing.operation.matrix.Matrix3)3 CoordinateSystem (org.opengis.referencing.cs.CoordinateSystem)3 FactoryException (org.opengis.util.FactoryException)3 DirectPosition2D (org.apache.sis.geometry.DirectPosition2D)2 DirectPositionView (org.apache.sis.internal.referencing.DirectPositionView)2 Parameterized (org.apache.sis.parameter.Parameterized)2 MatrixSIS (org.apache.sis.referencing.operation.matrix.MatrixSIS)2 GeneralParameterValue (org.opengis.parameter.GeneralParameterValue)2 ParameterValue (org.opengis.parameter.ParameterValue)2