blob: 662d94215cb5881399f0a1231799d407e0f53aad [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sis.referencing.datum;
import java.time.Instant;
import java.time.temporal.Temporal;
import java.time.temporal.ChronoField;
import org.opengis.referencing.operation.Matrix;
import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.matrix.MatrixSIS;
import org.apache.sis.referencing.operation.matrix.NoninvertibleMatrixException;
import static org.apache.sis.util.privy.Constants.MILLIS_PER_TROPICAL_YEAR;
// Test dependencies
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import org.apache.sis.test.TestCase;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
import static org.opengis.test.Assertions.assertMatrixEquals;
/**
* Tests the {@link TimeDependentBWP} class.
*
* @author Martin Desruisseaux (Geomatys)
*/
public final class TimeDependentBWPTest extends TestCase {
/**
* Creates a new test case.
*/
public TimeDependentBWPTest() {
}
/**
* Creates a {@code TimeDependentBWP} using the example given in the EPSG database for operation method EPSG:1053.
* The target datum given by the EPG example is actually GDA94, but it is coincident with WGS84 to within 1 metre.
* For the purpose of this test, the target datum does not matter anyway.
*/
private static TimeDependentBWP create() {
final var p = new TimeDependentBWP(GeodeticDatumMock.WGS84, null, Instant.parse("1994-01-01T00:00:00Z"));
p.tX = -0.08468; p.dtX = +1.42;
p.tY = -0.01942; p.dtY = +1.34;
p.tZ = +0.03201; p.dtZ = +0.90;
p.rX = +0.0004254; p.drX = -1.5461;
p.rY = -0.0022578; p.drY = -1.1820;
p.rZ = -0.0024015; p.drZ = -1.1551;
p.dS = +0.00971; p.ddS = +0.000109;
return p;
}
/**
* Tests {@link TimeDependentBWP#invert()}. This will indirectly tests {@link TimeDependentBWP#getValues()}
* followed by {@link TimeDependentBWP#setValues(double[])} because of the way the {@code invert()} method
* is implemented.
*/
@Test
public void testInvert() {
/*
* Opportunistically test getValue() first because it is used by TimeDependentBWP.invert().
*/
final double[] expected = {
-0.08468, -0.01942, +0.03201, +0.0004254, -0.0022578, -0.0024015, +0.00971,
+1.42, +1.34, +0.90, -1.5461, -1.1820, -1.1551, +0.000109
};
final TimeDependentBWP p = create();
assertArrayEquals(expected, p.getValues());
/*
* Now perform the actual TimeDependentBWP.invert() test.
*/
for (int i=0; i<expected.length; i++) {
expected[i] = -expected[i];
}
p.invert();
assertArrayEquals(expected, p.getValues());
}
/**
* Tests the {@link TimeDependentBWP#setPositionVectorTransformation(Matrix, double)} method
* using the example given in the EPSG database for operation method EPSG:1053.
*
* @throws NoninvertibleMatrixException Should not happen.
*/
@Test
public void testSetPositionVectorTransformation() throws NoninvertibleMatrixException {
/*
* The transformation that we are going to test use as input
* geocentric coordinates on ITRF2008 at epoch 2013.9.
*/
final TimeDependentBWP p = create();
Temporal time = p.getTimeReference();
long t = time.getLong(ChronoField.INSTANT_SECONDS);
t += StrictMath.round((2013.9 - 1994) * (MILLIS_PER_TROPICAL_YEAR / 1000.0));
time = Instant.ofEpochSecond(t);
assertEquals(Instant.parse("2013-11-25T07:40:16Z"), time);
/*
* Transform the point given in the EPSG example and compare with the coordinate
* that we obtain if we do the calculation ourself using the intermediate values
* given in EPSG example.
*/
final MatrixSIS toGDA94 = MatrixSIS.castOrCopy(p.getPositionVectorTransformation(time));
final MatrixSIS toITRF2008 = toGDA94.inverse();
final MatrixSIS source = Matrices.create(4, 1, new double[] {-3789470.702, 4841770.411, -1690893.950, 1});
final MatrixSIS target = Matrices.create(4, 1, new double[] {-3789470.008, 4841770.685, -1690895.103, 1});
final MatrixSIS actual = toGDA94.multiply(source);
compareWithExplicitCalculation(actual);
/*
* Now compare with the expected value given in the EPSG example. The 0.013 tolerance threshold
* is for the X coordinate and has been determined empirically in testEpsgCalculation().
*/
assertMatrixEquals(target, actual, 0.013, "toGDA94");
assertMatrixEquals(source, toITRF2008.multiply(target), 0.013, "toITRF2008");
}
/**
* Compares the coordinates calculated with the {@link TimeDependentBWP#getPositionVectorTransformation(Temporal)}
* matrix with the coordinates calculated ourselves from the numbers given in the EPSG examples. Note that the
* EPSG documentation truncates the numerical values given in their example, so it is normal that we have a
* slight difference.
*
* @param actual the coordinates calculated by the matrix, or {@code null} for comparing against
* the EPSG expected values.
*/
private void compareWithExplicitCalculation(final Matrix actual) {
/*
* Following are Bursa-Wolf parameters corrected for the rate of changes. The numerical values here
* are different than the numerical values in testSetPositionVectorTransformation() because of this
* correction. Units are metres and radians.
*/
final double tX = -0.056;
final double tY = +0.007;
final double tZ = +0.050;
final double rX = -1.471021E-07;
final double rY = -1.249830E-07;
final double rZ = -1.230844E-07;
final double dS = +0.01188;
/*
* The source coordinates. This is the same coordinates as testSetPositionVectorTransformation().
*/
final double Xs = -3789470.702;
final double Ys = 4841770.411;
final double Zs = -1690893.950;
/*
* Application of the 7 parameter Position Vector transformation.
* (the multiplications by 1 are like no-op, but kept for visualizing the matrix terms).
*/
final double M = 1 + (dS * 1E-6);
final double Xt = M * ( 1 * Xs + -rZ * Ys + rY * Zs) + tX;
final double Yt = M * (+rZ * Xs + 1 * Ys + -rX * Zs) + tY;
final double Zt = M * (-rY * Xs + rX * Ys + 1 * Zs) + tZ;
/*
* Compares with the coordinates calculated by the TimeDependentBWP matrix (first case), or
* with the expected coordinates provided by EPSG (second case). See testEpsgCalculation()
* for a discussion about the second case.
*/
if (actual != null) {
assertEquals(Xt, actual.getElement(0, 0), 0.0005);
assertEquals(Yt, actual.getElement(1, 0), 0.0005);
assertEquals(Zt, actual.getElement(2, 0), 0.0005);
} else {
assertEquals(-3789470.008, Xt, 0.013); // Smallest tolerance value such as the test do not fail.
assertEquals( 4841770.685, Yt, 0.009);
assertEquals(-1690895.103, Zt, 0.003);
}
}
/**
* Tries to apply ourselves the example given for operation method EPSG:1053 in EPSG database 8.2.7.
* For a unknown reason, we do not get exactly the values that EPSG said that we should get when applying
* the 7 parameter Position Vector transformation from the parameter values as corrected by EPSG themselves.
* We find a difference of 13, 9 and 3 millimetres along the X, Y and Z axis respectively.
*
* <p>The purpose of this test is to ensure that we get the same errors when calculating from the corrected values
* provided by EPSG, rather than as an error in {@link TimeDependentBWP#getPositionVectorTransformation(Temporal)}</p>
*/
@Test
public void testEpsgCalculation() {
compareWithExplicitCalculation(null);
}
}