blob: b159f99d3a303a7c5bd04d8c58e6c2e415544ebd [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.operation.transform;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.Collections;
import org.opengis.util.FactoryException;
import org.opengis.util.NoSuchIdentifierException;
import org.opengis.referencing.operation.Conversion;
import org.opengis.referencing.operation.Projection;
import org.opengis.referencing.operation.SingleOperation;
import org.opengis.referencing.operation.OperationMethod;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransformFactory;
import org.opengis.parameter.ParameterValueGroup;
import org.apache.sis.parameter.Parameterized;
import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.referencing.operation.DefaultConversion;
import org.apache.sis.referencing.operation.matrix.Matrix2;
import org.apache.sis.referencing.crs.DefaultProjectedCRS;
import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
import org.apache.sis.internal.referencing.provider.Affine;
import org.apache.sis.internal.referencing.provider.Mercator1SP;
import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.util.Constants;
import org.apache.sis.measure.Units;
// Test dependencies
import org.apache.sis.referencing.cs.HardCodedCS;
import org.apache.sis.test.DependsOnMethod;
import org.apache.sis.test.DependsOn;
import org.apache.sis.test.TestCase;
import org.junit.Test;
import static org.apache.sis.test.Assert.*;
/**
* Tests the registration of operation methods in {@link DefaultMathTransformFactory}. This test uses the
* providers registered in all {@code META-INF/services/org.opengis.referencing.operation.OperationMethod}
* files found on the classpath.
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.1
* @since 0.6
* @module
*/
@DependsOn({
org.apache.sis.internal.referencing.provider.ProvidersTest.class,
OperationMethodSetTest.class
})
public final strictfp class DefaultMathTransformFactoryTest extends TestCase {
/**
* Returns the factory to use for the tests.
*
* @return the factory to use for the tests.
*/
static DefaultMathTransformFactory factory() {
final MathTransformFactory factory = DefaultFactories.forClass(MathTransformFactory.class);
assertNotNull("No Apache SIS implementation of MathTransformFactory found in “META-INF/services”.", factory);
assertEquals("Expected the default implementation of MathTransformFactory to be first in “META-INF/services”.",
DefaultMathTransformFactory.class, factory.getClass());
return (DefaultMathTransformFactory) factory;
}
/**
* Tests the {@link DefaultMathTransformFactory#getOperationMethod(String)} method.
*
* @throws NoSuchIdentifierException if the operation was not found.
*/
@Test
public void testGetOperationMethod() throws NoSuchIdentifierException {
final DefaultMathTransformFactory factory = factory();
final OperationMethod affine = factory.getOperationMethod(Constants.AFFINE);
final OperationMethod mercator = factory.getOperationMethod("Mercator (variant A)");
assertInstanceOf("Affine", Affine.class, affine);
assertInstanceOf("Mercator (variant A)", Mercator1SP.class, mercator);
// Same than above, using EPSG code and alias.
assertSame("EPSG:9624", affine, factory.getOperationMethod("EPSG:9624"));
assertSame("EPSG:9804", mercator, factory.getOperationMethod("EPSG:9804"));
assertSame("Mercator_1SP", mercator, factory.getOperationMethod("Mercator_1SP"));
}
/**
* Tests non-existent operation method.
*/
@Test
@DependsOnMethod("testGetOperationMethod")
public void testNonExistentCode() {
final DefaultMathTransformFactory factory = factory();
try {
factory.getOperationMethod("EPXX:9624");
fail("Expected NoSuchIdentifierException");
} catch (NoSuchIdentifierException e) {
final String message = e.getLocalizedMessage();
assertTrue(message, message.contains("EPXX:9624"));
}
}
/**
* Tests the {@link DefaultMathTransformFactory#getAvailableMethods(Class)} method.
*
* @throws NoSuchIdentifierException if the operation was not found.
*/
@Test
@DependsOnMethod("testGetOperationMethod")
public void testGetAvailableMethods() throws NoSuchIdentifierException {
final DefaultMathTransformFactory factory = factory();
final Set<OperationMethod> transforms = factory.getAvailableMethods(SingleOperation.class);
final Set<OperationMethod> conversions = factory.getAvailableMethods(Conversion.class);
final Set<OperationMethod> projections = factory.getAvailableMethods(Projection.class);
/*
* Following tests should not cause loading of more classes than needed.
*/
assertFalse(transforms .isEmpty());
assertFalse(conversions.isEmpty());
assertFalse(projections.isEmpty());
assertTrue (transforms.contains(factory.getOperationMethod(Constants.AFFINE)));
/*
* Following tests will force instantiation of all remaining OperationMethod.
*/
assertTrue("Conversions should be a subset of transforms.", transforms .containsAll(conversions));
assertTrue("Projections should be a subset of conversions.", conversions.containsAll(projections));
}
/**
* Asks for names which are known to be duplicated. One of the duplicated elements is deprecated.
* However Apache SIS uses the same implementation.
*
* @throws NoSuchIdentifierException if the operation was not found.
*/
@Test
public void testDuplicatedNames() throws NoSuchIdentifierException {
final DefaultMathTransformFactory factory = factory();
final OperationMethod current = factory.getOperationMethod("EPSG:1029");
final OperationMethod deprecated = factory.getOperationMethod("EPSG:9823");
assertSame(current, factory.getOperationMethod("Equidistant Cylindrical (Spherical)"));
assertSame("Should share the non-deprecated implementation.", current, deprecated);
}
/**
* Test {@link DefaultMathTransformFactory#createFromWKT(String)}. We test only a very small WKT here because
* it is not the purpose of this class to test the parser. The main purpose of this test is to verify that
* {@link DefaultMathTransformFactory} has been able to instantiate the parser.
*
* @throws FactoryException if the parsing failed.
*/
@Test
public void testCreateFromWKT() throws FactoryException {
final MathTransform tr = factory().createFromWKT(
"PARAM_MT[\"Affine\","
+ "PARAMETER[\"num_row\",2],"
+ "PARAMETER[\"num_col\",2],"
+ "PARAMETER[\"elt_0_1\",7]]");
assertMatrixEquals("Affine", new Matrix2(
1, 7,
0, 1), MathTransforms.getMatrix(tr), STRICT);
}
/**
* Tests the creation of all registered map projections.
* This test sets the semi-axis lengths and a few other mandatory parameter values.
* For remaining parameters, we rely on default values.
*
* @throws FactoryException if the construction of a map projection failed.
*
* @since 0.7
*/
@Test
@SuppressWarnings("fallthrough")
public void testAllMapProjections() throws FactoryException {
/*
* Gets all map projections and creates a projection using the WGS84 ellipsoid
* and default parameter values.
*/
final Map<String,?> dummyName = Collections.singletonMap(DefaultProjectedCRS.NAME_KEY, "Test");
final MathTransformFactory mtFactory = DefaultFactories.forBuildin(MathTransformFactory.class);
final Collection<OperationMethod> methods = mtFactory.getAvailableMethods(Projection.class);
for (final OperationMethod method : methods) {
final String classification = method.getName().getCode();
ParameterValueGroup pg = mtFactory.getDefaultParameters(classification);
pg.parameter("semi_major").setValue(6377563.396);
pg.parameter("semi_minor").setValue(6356256.909237285);
/*
* Most parameters have default values, typically 0° or 0 metre, so we don't need to specify them for
* the purpose of this test. But some map projections have mandatory parameters without default value.
* In those cases, we must specify an arbitrary value otherwise the instantiation will fail. When we
* need to specify only one value, that value is remembered for opportunist verification.
*/
String param = null;
double value = Double.NaN;
switch (classification) {
case "Polar Stereographic (variant A)": param = "Latitude of natural origin"; value = 90; break;
case "Polar Stereographic (variant B)":
case "Polar Stereographic (variant C)": param = "Latitude of standard parallel"; value = 80; break;
case "Hotine Oblique Mercator (variant A)":
case "Hotine Oblique Mercator (variant B)": param = "Azimuth of initial line"; value = 30; break;
case "Lambert Conic Conformal (1SP)":
case "Lambert Conic Conformal (West Orientated)": param = "Latitude of natural origin"; value = 45; break;
case "Lambert Conic Conformal (2SP Michigan)": param = "Ellipsoid scaling factor"; value = 1; // Fall through for defining standard parallels too.
case "Lambert Conic Conformal (2SP Belgium)":
case "Lambert Conic Conformal (2SP)":
case "Albers Equal Area": {
pg.parameter("Latitude of 1st standard parallel").setValue(30);
pg.parameter("Latitude of 2nd standard parallel").setValue(50);
break;
}
case "Hotine_Oblique_Mercator_Two_Point_Center":
case "Hotine_Oblique_Mercator_Two_Point_Natural_Origin": {
pg.parameter( "Latitude_Of_1st_Point").setValue(30);
pg.parameter( "Latitude_Of_2nd_Point").setValue(50);
pg.parameter("Longitude_Of_1st_Point").setValue(10);
pg.parameter("Longitude_Of_2nd_Point").setValue(20);
break;
}
case "Satellite-Tracking": {
pg.parameter("satellite_orbit_inclination").setValue( 99.092); // Landsat 1, 2 and 3.
pg.parameter("satellite_orbital_period") .setValue( 103.267, Units.MINUTE);
pg.parameter("ascending_node_period") .setValue(1440.0, Units.MINUTE);
break;
}
}
if (param != null) {
pg.parameter(param).setValue(value);
}
final MathTransform mt;
try {
mt = mtFactory.createParameterizedTransform(pg);
} catch (InvalidGeodeticParameterException e) {
fail(classification + ": " + e.getLocalizedMessage());
continue;
}
/*
* Verifies that the map projection properties are the ones that we specified.
* Note that the Equirectangular projection has been optimized as an affine transform, which we skip.
*/
if (mt instanceof LinearTransform) {
continue;
}
assertInstanceOf(classification, Parameterized.class, mt);
pg = ((Parameterized) mt).getParameterValues();
assertNotNull(classification, pg);
assertEquals(classification, pg.getDescriptor().getName().getCode());
assertEquals(classification, 6377563.396, pg.parameter("semi_major").doubleValue(), 1E-4);
assertEquals(classification, 6356256.909237285, pg.parameter("semi_minor").doubleValue(), 1E-4);
if (param != null) {
assertEquals(classification, value, pg.parameter(param).doubleValue(), 1E-4);
}
/*
* Creates a ProjectedCRS from the map projection. This part is more an integration test than
* a DefaultMathTransformFactory test. Again, the intent is to verify that the properties are
* the one that we specified.
*/
final DefaultProjectedCRS crs = new DefaultProjectedCRS(dummyName,
CommonCRS.WGS84.normalizedGeographic(),
new DefaultConversion(dummyName, method, mt, null),
HardCodedCS.PROJECTED);
final Conversion projection = crs.getConversionFromBase();
assertSame(classification, mt, projection.getMathTransform());
assertEquals(classification, projection.getMethod().getName().getCode());
}
}
}