Author: desruisseaux
Date: Sat Mar 31 15:17:48 2018
New Revision: 1828112
URL: http://svn.apache.org/viewvc?rev=1828112&view=rev
Log:
Allow CoordinateOperationFinder to return a list of CoordinateOperation instead
than a singleton (SIS-412).
Added:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationSorter.java
(with props)
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java?rev=1828112&r1=1828111&r2=1828112&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
[UTF-8] Sat Mar 31 15:17:48 2018
@@ -19,7 +19,9 @@ package org.apache.sis.referencing.opera
import java.util.Map;
import java.util.HashMap;
import java.util.List;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.ListIterator;
import javax.measure.Unit;
import javax.measure.quantity.Time;
import javax.measure.IncommensurableException;
@@ -108,7 +110,7 @@ import static org.apache.sis.util.Utilit
* </ul>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
*
* @see
DefaultCoordinateOperationFactory#createOperation(CoordinateReferenceSystem,
CoordinateReferenceSystem, CoordinateOperationContext)
*
@@ -165,7 +167,39 @@ public class CoordinateOperationFinder e
/**
* Infers an operation for conversion or transformation between two
coordinate reference systems.
* If a non-null authority factory – the <cite>registry</cite> – has been
specified at construction time,
- * this method will first query that factory (<cite>late-binding</cite>
approach – see class javadoc).
+ * then this method will first query that factory
(<cite>late-binding</cite> approach – see class javadoc).
+ * If no operation has been found in the registry or if no registry has
been specified to the constructor,
+ * this method inspects the given CRS and delegates the work to one or
many {@code createOperationStep(…)}
+ * methods (<cite>early-binding</cite> approach).
+ *
+ * <p>The default implementation invokes <code>{@linkplain
#createOperations createOperations}(sourceCRS,
+ * targetCRS)</code>, then returns the first operation in the returned
list or throws an exception if the
+ * list is empty.</p>
+ *
+ * @param sourceCRS input coordinate reference system.
+ * @param targetCRS output coordinate reference system.
+ * @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS}.
+ * @throws OperationNotFoundException if no operation path was found from
{@code sourceCRS} to {@code targetCRS}.
+ * @throws FactoryException if the operation creation failed for some
other reason.
+ */
+ public CoordinateOperation createOperation(final CoordinateReferenceSystem
sourceCRS,
+ final CoordinateReferenceSystem
targetCRS)
+ throws OperationNotFoundException, FactoryException
+ {
+ final boolean oldState = stopAtFirst;
+ stopAtFirst = true;
+ final List<CoordinateOperation> operations =
createOperations(sourceCRS, targetCRS);
+ stopAtFirst = oldState;
+ if (!operations.isEmpty()) {
+ return operations.get(0);
+ }
+ throw new OperationNotFoundException(notFoundMessage(sourceCRS,
targetCRS));
+ }
+
+ /**
+ * Infers operations for conversions or transformations between two
coordinate reference systems.
+ * If a non-null authority factory – the <cite>registry</cite> – has been
specified at construction time,
+ * then this method will first query that factory
(<cite>late-binding</cite> approach – see class javadoc).
* If no operation has been found in the registry or if no registry has
been specified to the constructor,
* this method inspects the given CRS and delegates the work to one or
many {@code createOperationStep(…)}
* methods (<cite>early-binding</cite> approach).
@@ -177,22 +211,29 @@ public class CoordinateOperationFinder e
* for example in order to process the {@linkplain
org.apache.sis.referencing.crs.DefaultProjectedCRS#getBaseCRS()
* base geographic CRS} of a projected CRS.</p>
*
+ * <p>Coordinate operations are returned in preference order: best
operations for the area of interest should be first.
+ * The returned list is modifiable: callers can add, remove or set
elements without impact on this
+ * {@code CoordinateOperationFinder} instance.</p>
+ *
* @param sourceCRS input coordinate reference system.
* @param targetCRS output coordinate reference system.
- * @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS}.
+ * @return coordinate operations from {@code sourceCRS} to {@code
targetCRS}.
* @throws OperationNotFoundException if no operation path was found from
{@code sourceCRS} to {@code targetCRS}.
* @throws FactoryException if the operation creation failed for some
other reason.
+ *
+ * @since 1.0
*/
@Override
- public CoordinateOperation createOperation(final CoordinateReferenceSystem
sourceCRS,
- final CoordinateReferenceSystem
targetCRS)
- throws OperationNotFoundException, FactoryException
+ public List<CoordinateOperation> createOperations(final
CoordinateReferenceSystem sourceCRS,
+ final
CoordinateReferenceSystem targetCRS)
+ throws FactoryException
{
ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS);
ArgumentChecks.ensureNonNull("targetCRS", targetCRS);
if (equalsIgnoreMetadata(sourceCRS, targetCRS)) try {
- return createFromAffineTransform(AXIS_CHANGES, sourceCRS,
targetCRS,
-
CoordinateSystems.swapAndScaleAxes(sourceCRS.getCoordinateSystem(),
targetCRS.getCoordinateSystem()));
+ return asList(createFromAffineTransform(AXIS_CHANGES, sourceCRS,
targetCRS,
+
CoordinateSystems.swapAndScaleAxes(sourceCRS.getCoordinateSystem(),
+
targetCRS.getCoordinateSystem())));
} catch (IllegalArgumentException | IncommensurableException e) {
throw new
FactoryException(Resources.format(Resources.Keys.CanNotInstantiateGeodeticObject_1,
new CRSPair(sourceCRS, targetCRS)), e);
}
@@ -204,27 +245,15 @@ public class CoordinateOperationFinder e
* is not in the cache, store the key in our internal map for
preventing infinite recursivity.
*/
final CRSPair key = new CRSPair(sourceCRS, targetCRS);
- if (useCache && !previousSearches.isEmpty()) {
+ if (useCache && stopAtFirst && !previousSearches.isEmpty()) {
final CoordinateOperation op = factorySIS.cache.peek(key);
- if (op != null) return op;
+ if (op != null) return asList(op); // Must be a modifiable
list as per this method contract.
}
if (previousSearches.put(key, Boolean.TRUE) != null) {
throw new
FactoryException(Resources.format(Resources.Keys.RecursiveCreateCallForCode_2,
CoordinateOperation.class, key));
}
/*
- * Verify if some extension module handles this pair of CRS in a
special way. For example it may
- * be the "sis-gdal" module checking if the given CRS are wrappers
around Proj.4 data structure.
- */
- for (final SpecializedOperationFactory sp :
factorySIS.getSpecializedFactories()) {
- for (final CoordinateOperation op : sp.findOperations(sourceCRS,
targetCRS)) {
- if (filter(op)) {
- return op;
- }
- }
- }
- /*
* If the user did not specified an area of interest, use the domain
of validity of the CRS.
- * Then verify in the EPSG dataset if the operation is explicitely
defined by an authority.
*/
GeographicBoundingBox bbox =
Extents.getGeographicBoundingBox(areaOfInterest);
if (bbox == null) {
@@ -232,9 +261,30 @@ public class CoordinateOperationFinder e
CRS.getGeographicBoundingBox(targetCRS));
areaOfInterest =
CoordinateOperationContext.setGeographicBoundingBox(areaOfInterest, bbox);
}
+ /*
+ * Verify if some extension module handles this pair of CRS in a
special way. For example it may
+ * be the "sis-gdal" module checking if the given CRS are wrappers
around Proj.4 data structure.
+ */
+ { // For keeping 'operations' list locale.
+ final List<CoordinateOperation> operations = new ArrayList<>();
+ for (final SpecializedOperationFactory sp :
factorySIS.getSpecializedFactories()) {
+ for (final CoordinateOperation op :
sp.findOperations(sourceCRS, targetCRS)) {
+ if (filter(op)) {
+ operations.add(op);
+ }
+ }
+ }
+ if (!operations.isEmpty()) {
+ CoordinateOperationSorter.sort(operations, bbox);
+ return operations;
+ }
+ }
+ /*
+ * Verify in the EPSG dataset if the operation is explicitely defined
by an authority.
+ */
if (registry != null) {
- final CoordinateOperation op = super.createOperation(sourceCRS,
targetCRS);
- if (op != null) return op;
+ final List<CoordinateOperation> authoritatives =
super.createOperations(sourceCRS, targetCRS);
+ if (!authoritatives.isEmpty()) return authoritatives;
}
////////////////////////////////////////////////////////////////////////////////
////
////
@@ -310,7 +360,7 @@ public class CoordinateOperationFinder e
}
/**
- * Creates an operation from an arbitrary single CRS to a derived
coordinate reference system.
+ * Creates operations from an arbitrary single CRS to a derived coordinate
reference system.
* Conversions from {@code GeographicCRS} to {@code ProjectedCRS} are also
handled by this method,
* since projected CRS are a special kind of {@code GeneralDerivedCRS}.
*
@@ -320,18 +370,29 @@ public class CoordinateOperationFinder e
* where the conversion from {@code baseCRS} to {@code targetCRS} is
obtained from
* <code>targetCRS.{@linkplain GeneralDerivedCRS#getConversionFromBase()
getConversionFromBase()}</code>.
*
+ * <p>This method returns only <em>one</em> step for a chain of
concatenated operations (to be built by the caller).
+ * But a list is returned because the same step may be implemented by
different operation methods. Only one element
+ * in the returned list should be selected (usually the first one).</p>
+ *
* @param sourceCRS input coordinate reference system.
* @param targetCRS output coordinate reference system.
- * @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS}.
+ * @return coordinate operations from {@code sourceCRS} to {@code
targetCRS}.
* @throws FactoryException if the operation can not be constructed.
*/
- protected CoordinateOperation createOperationStep(final SingleCRS
sourceCRS,
- final GeneralDerivedCRS
targetCRS)
+ protected List<CoordinateOperation> createOperationStep(final SingleCRS
sourceCRS,
+ final
GeneralDerivedCRS targetCRS)
throws FactoryException
{
- final CoordinateOperation step1 = createOperation(sourceCRS,
targetCRS.getBaseCRS());
- final CoordinateOperation step2 = targetCRS.getConversionFromBase();
- return concatenate(step1, step2);
+ final List<CoordinateOperation> operations =
createOperations(sourceCRS, targetCRS.getBaseCRS());
+ final ListIterator<CoordinateOperation> it = operations.listIterator();
+ if (it.hasNext()) {
+ final CoordinateOperation step2 =
targetCRS.getConversionFromBase();
+ do {
+ final CoordinateOperation step1 = it.next();
+ it.set(concatenate(step1, step2));
+ } while (it.hasNext());
+ }
+ return operations;
}
/**
@@ -345,26 +406,36 @@ public class CoordinateOperationFinder e
* where the conversion from {@code sourceCRS} to {@code baseCRS} is
obtained from the inverse of
* <code>sourceCRS.{@linkplain GeneralDerivedCRS#getConversionFromBase()
getConversionFromBase()}</code>.
*
+ * <p>This method returns only <em>one</em> step for a chain of
concatenated operations (to be built by the caller).
+ * But a list is returned because the same step may be implemented by
different operation methods. Only one element
+ * in the returned list should be selected (usually the first one).</p>
+ *
* @param sourceCRS input coordinate reference system.
* @param targetCRS output coordinate reference system.
* @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS}.
* @throws FactoryException if the operation can not be constructed.
*/
- protected CoordinateOperation createOperationStep(final GeneralDerivedCRS
sourceCRS,
- final SingleCRS
targetCRS)
+ protected List<CoordinateOperation> createOperationStep(final
GeneralDerivedCRS sourceCRS,
+ final SingleCRS
targetCRS)
throws FactoryException
{
- // Create first the operation that is more at risk to fail.
- final CoordinateOperation step2 =
createOperation(sourceCRS.getBaseCRS(), targetCRS);
- final CoordinateOperation step1;
- try {
- step1 = inverse(sourceCRS.getConversionFromBase());
- } catch (OperationNotFoundException exception) {
- throw exception;
- } catch (FactoryException | NoninvertibleTransformException exception)
{
- throw new OperationNotFoundException(canNotInvert(sourceCRS),
exception);
+ final List<CoordinateOperation> operations =
createOperations(sourceCRS.getBaseCRS(), targetCRS);
+ final ListIterator<CoordinateOperation> it = operations.listIterator();
+ if (it.hasNext()) {
+ final CoordinateOperation step1;
+ try {
+ step1 = inverse(sourceCRS.getConversionFromBase());
+ } catch (OperationNotFoundException exception) {
+ throw exception;
+ } catch (FactoryException | NoninvertibleTransformException
exception) {
+ throw new OperationNotFoundException(canNotInvert(sourceCRS),
exception);
+ }
+ do {
+ final CoordinateOperation step2 = it.next();
+ it.set(concatenate(step1, step2));
+ } while (it.hasNext());
}
- return concatenate(step1, step2);
+ return operations;
}
/**
@@ -377,27 +448,37 @@ public class CoordinateOperationFinder e
* <li>Convert from the target base CRS to the {@code targetCRS}.</li>
* </ol>
*
+ * <p>This method returns only <em>one</em> step for a chain of
concatenated operations (to be built by the caller).
+ * But a list is returned because the same step may be implemented by
different operation methods. Only one element
+ * in the returned list should be selected (usually the first one).</p>
+ *
* @param sourceCRS input coordinate reference system.
* @param targetCRS output coordinate reference system.
* @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS}.
* @throws FactoryException if the operation can not be constructed.
*/
- protected CoordinateOperation createOperationStep(final GeneralDerivedCRS
sourceCRS,
- final GeneralDerivedCRS
targetCRS)
+ protected List<CoordinateOperation> createOperationStep(final
GeneralDerivedCRS sourceCRS,
+ final
GeneralDerivedCRS targetCRS)
throws FactoryException
{
- // Create first the operations that are more at risk to fail.
- final CoordinateOperation step2 =
createOperation(sourceCRS.getBaseCRS(), targetCRS.getBaseCRS());
- final CoordinateOperation step3 = targetCRS.getConversionFromBase();
- final CoordinateOperation step1;
- try {
- step1 = inverse(sourceCRS.getConversionFromBase());
- } catch (OperationNotFoundException exception) {
- throw exception;
- } catch (FactoryException | NoninvertibleTransformException exception)
{
- throw new OperationNotFoundException(canNotInvert(sourceCRS),
exception);
+ final List<CoordinateOperation> operations =
createOperations(sourceCRS.getBaseCRS(), targetCRS.getBaseCRS());
+ final ListIterator<CoordinateOperation> it = operations.listIterator();
+ if (it.hasNext()) {
+ final CoordinateOperation step3 =
targetCRS.getConversionFromBase();
+ final CoordinateOperation step1;
+ try {
+ step1 = inverse(sourceCRS.getConversionFromBase());
+ } catch (OperationNotFoundException exception) {
+ throw exception;
+ } catch (FactoryException | NoninvertibleTransformException
exception) {
+ throw new OperationNotFoundException(canNotInvert(sourceCRS),
exception);
+ }
+ do {
+ final CoordinateOperation step2 = it.next();
+ it.set(concatenate(step1, step2, step3));
+ } while (it.hasNext());
}
- return concatenate(step1, step2, step3);
+ return operations;
}
/**
@@ -413,14 +494,18 @@ public class CoordinateOperationFinder e
* for the area of interest.</li>
* </ul>
*
+ * <p>This method returns only <em>one</em> step for a chain of
concatenated operations (to be built by the caller).
+ * But a list is returned because the same step may be implemented by
different operation methods. Only one element
+ * in the returned list should be selected (usually the first one).</p>
+ *
* @param sourceCRS input coordinate reference system.
* @param targetCRS output coordinate reference system.
* @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS}.
* @throws FactoryException if the operation can not be constructed.
*/
@SuppressWarnings("null")
- protected CoordinateOperation createOperationStep(final GeodeticCRS
sourceCRS,
- final GeodeticCRS
targetCRS)
+ protected List<CoordinateOperation> createOperationStep(final GeodeticCRS
sourceCRS,
+ final GeodeticCRS
targetCRS)
throws FactoryException
{
final GeodeticDatum sourceDatum = sourceCRS.getDatum();
@@ -577,7 +662,7 @@ public class CoordinateOperationFinder e
transform = mtFactory.createConcatenatedTransform(transform,
after);
}
}
- return createFromMathTransform(properties(identifier), sourceCRS,
targetCRS, transform, method, parameters, null);
+ return asList(createFromMathTransform(properties(identifier),
sourceCRS, targetCRS, transform, method, parameters, null));
}
/**
@@ -585,13 +670,17 @@ public class CoordinateOperationFinder e
* The height returned by this method will usually be part of a
* {@linkplain DefaultPassThroughOperation pass-through operation}.
*
+ * <p>This method returns only <em>one</em> step for a chain of
concatenated operations (to be built by the caller).
+ * But a list is returned because the same step may be implemented by
different operation methods. Only one element
+ * in the returned list should be selected (usually the first one).</p>
+ *
* @param sourceCRS input coordinate reference system.
* @param targetCRS output coordinate reference system.
* @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS}.
* @throws FactoryException if the operation can not be constructed.
*/
- protected CoordinateOperation createOperationStep(final GeodeticCRS
sourceCRS,
- final VerticalCRS
targetCRS)
+ protected List<CoordinateOperation> createOperationStep(final GeodeticCRS
sourceCRS,
+ final VerticalCRS
targetCRS)
throws FactoryException
{
/*
@@ -676,7 +765,7 @@ public class CoordinateOperationFinder e
matrix.setElement(0, i, 1);
// Scale factor for height.
matrix.setElement(tgtDim, srcDim, 1);
// Always 1 for affine transform.
step2 = createFromAffineTransform(AXIS_CHANGES, interpolationCRS,
heightCRS, matrix);
- return concatenate(step1, step2, step3);
+ return asList(concatenate(step1, step2, step3));
}
/**
@@ -684,6 +773,10 @@ public class CoordinateOperationFinder e
* The default implementation checks if both CRS use the same datum, then
* adjusts for axis direction and units.
*
+ * <p>This method returns only <em>one</em> step for a chain of
concatenated operations (to be built by the caller).
+ * But a list is returned because the same step may be implemented by
different operation methods. Only one element
+ * in the returned list should be selected (usually the first one).</p>
+ *
* @param sourceCRS input coordinate reference system.
* @param targetCRS output coordinate reference system.
* @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS}.
@@ -691,8 +784,8 @@ public class CoordinateOperationFinder e
*
* @todo Needs to implement vertical datum shift.
*/
- protected CoordinateOperation createOperationStep(final VerticalCRS
sourceCRS,
- final VerticalCRS
targetCRS)
+ protected List<CoordinateOperation> createOperationStep(final VerticalCRS
sourceCRS,
+ final VerticalCRS
targetCRS)
throws FactoryException
{
final VerticalDatum sourceDatum = sourceCRS.getDatum();
@@ -708,7 +801,7 @@ public class CoordinateOperationFinder e
} catch (IllegalArgumentException | IncommensurableException
exception) {
throw new OperationNotFoundException(notFoundMessage(sourceCRS,
targetCRS), exception);
}
- return createFromAffineTransform(AXIS_CHANGES, sourceCRS, targetCRS,
matrix);
+ return asList(createFromAffineTransform(AXIS_CHANGES, sourceCRS,
targetCRS, matrix));
}
/**
@@ -716,13 +809,17 @@ public class CoordinateOperationFinder e
* The default implementation checks if both CRS use the same datum, then
* adjusts for axis direction, units and epoch.
*
+ * <p>This method returns only <em>one</em> step for a chain of
concatenated operations (to be built by the caller).
+ * But a list is returned because the same step may be implemented by
different operation methods. Only one element
+ * in the returned list should be selected (usually the first one).</p>
+ *
* @param sourceCRS input coordinate reference system.
* @param targetCRS output coordinate reference system.
* @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS}.
* @throws FactoryException if the operation can not be constructed.
*/
- protected CoordinateOperation createOperationStep(final TemporalCRS
sourceCRS,
- final TemporalCRS
targetCRS)
+ protected List<CoordinateOperation> createOperationStep(final TemporalCRS
sourceCRS,
+ final TemporalCRS
targetCRS)
throws FactoryException
{
final TemporalDatum sourceDatum = sourceCRS.getDatum();
@@ -757,7 +854,7 @@ public class CoordinateOperationFinder e
final int translationColumn = matrix.getNumCol() - 1; //
Paranoiac check: should always be 1.
final double translation = matrix.getElement(0, translationColumn);
matrix.setElement(0, translationColumn, translation + epochShift);
- return createFromAffineTransform(AXIS_CHANGES, sourceCRS, targetCRS,
matrix);
+ return asList(createFromAffineTransform(AXIS_CHANGES, sourceCRS,
targetCRS, matrix));
}
/**
@@ -766,6 +863,10 @@ public class CoordinateOperationFinder e
* various combinations of source and target components. A preference is
given for components of the same
* type (e.g. source {@link GeodeticCRS} with target {@code GeodeticCRS},
<i>etc.</i>).
*
+ * <p>This method returns only <em>one</em> step for a chain of
concatenated operations (to be built by the caller).
+ * But a list is returned because the same step may be implemented by
different operation methods. Only one element
+ * in the returned list should be selected (usually the first one).</p>
+ *
* @param sourceCRS input coordinate reference system.
* @param sourceComponents components of the source CRS.
* @param targetCRS output coordinate reference system.
@@ -773,7 +874,7 @@ public class CoordinateOperationFinder e
* @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS}.
* @throws FactoryException if the operation can not be constructed.
*/
- protected CoordinateOperation createOperationStep(
+ protected List<CoordinateOperation> createOperationStep(
final CoordinateReferenceSystem sourceCRS, final List<? extends
SingleCRS> sourceComponents,
final CoordinateReferenceSystem targetCRS, final List<? extends
SingleCRS> targetComponents)
throws FactoryException
@@ -893,7 +994,7 @@ public class CoordinateOperationFinder e
endAtDimension -= delta;
remainingSourceDimensions -= delta;
}
- return operation;
+ return asList(operation);
}
@@ -1103,6 +1204,17 @@ public class CoordinateOperationFinder e
}
/**
+ * Returns the given operation as a list of one element. We can not use
{@link Collections#singletonList(Object)}
+ * because the list needs to be modifiable, as required by {@link
#createOperations(CoordinateReferenceSystem,
+ * CoordinateReferenceSystem)} method contract.
+ */
+ private static List<CoordinateOperation> asList(final CoordinateOperation
operation) {
+ final List<CoordinateOperation> operations = new ArrayList<>(1);
+ operations.add(operation);
+ return operations;
+ }
+
+ /**
* Returns an error message for "No path found from sourceCRS to
targetCRS".
* This is used for the construction of {@link OperationNotFoundException}.
*
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java?rev=1828112&r1=1828111&r2=1828112&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
[UTF-8] Sat Mar 31 15:17:48 2018
@@ -22,7 +22,7 @@ import java.util.List;
import java.util.ListIterator;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Iterator;
+import java.util.Collections;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.LogRecord;
@@ -94,7 +94,7 @@ import org.apache.sis.util.resources.Voc
* That approach is used only as a fallback when the late-binding
approach gave no result.</li>
* </ul>
*
- * When <code>{@linkplain #createOperation createOperation}(sourceCRS,
targetCRS)</code> is invoked,
+ * When <code>{@linkplain #createOperations createOperations}(sourceCRS,
targetCRS)</code> is invoked,
* this class fetches the authority codes for source and target CRS and
submits them to the authority factory
* through a call to its <code>{@linkplain
GeodeticAuthorityFactory#createFromCoordinateReferenceSystemCodes
* createFromCoordinateReferenceSystemCodes}(sourceCode, targetCode)</code>
method.
@@ -102,7 +102,7 @@ import org.apache.sis.util.resources.Voc
* then {@link CoordinateOperationFinder} will use its own fallback.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
* @since 0.7
* @module
*/
@@ -193,6 +193,13 @@ class CoordinateOperationRegistry {
protected double desiredAccuracy;
/**
+ * {@code true} if {@link #search(CoordinateReferenceSystem,
CoordinateReferenceSystem)}
+ * should stop after the first coordinate operation found. This field is
set to {@code true}
+ * when the caller is interested only in the "best" operation instead of
all of possibilities.
+ */
+ boolean stopAtFirst;
+
+ /**
* A filter that can be used for applying additional restrictions on the
coordinate operation,
* or {@code null} if none.
*/
@@ -279,29 +286,26 @@ class CoordinateOperationRegistry {
}
/**
- * Finds or infers an operation for conversion or transformation between
two coordinate reference systems.
+ * Finds or infers operations for conversions or transformations between
two coordinate reference systems.
* {@code CoordinateOperationRegistry} implements the
<cite>late-binding</cite> approach (see definition
* of terms in class javadoc) by extracting the authority codes from the
supplied {@code sourceCRS} and
* {@code targetCRS}, then by submitting those codes to the
* <code>{@linkplain
CoordinateOperationAuthorityFactory#createFromCoordinateReferenceSystemCodes
* createFromCoordinateReferenceSystemCodes}(sourceCode,
targetCode)</code> method.
- * If no operation is found for those codes, then this method returns
{@code null}.
- * Note that it does not mean that no path exist;
- * it only means that it was not defined explicitely in the registry.
*
- * <p>If the subclass implements the <cite>early-binding</cite> approach
(which is the fallback if late-binding
- * gave no result), then this method should never return {@code null}
since there is no other fallback.
- * Instead, this method may throw an {@link
OperationNotFoundException}.</p>
+ * <p>Operations in the returned list are ordered in preference order
(preferred operation first).
+ * If no operation is found for those codes, then this method returns an
empty list.
+ * Note that it does not mean that no path exist;
+ * it only means that it was not defined explicitely in the registry.</p>
*
* @param sourceCRS input coordinate reference system.
* @param targetCRS output coordinate reference system.
- * @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS},
- * or {@code null} if no such operation is explicitly defined in
the underlying database.
- * @throws OperationNotFoundException if no operation path was found from
{@code sourceCRS} to {@code targetCRS}.
- * @throws FactoryException if the operation creation failed for some
other reason.
+ * @return coordinate operations from {@code sourceCRS} to {@code
targetCRS},
+ * or an empty list if no such operation is explicitly defined in
the underlying database.
+ * @throws FactoryException if the operation creation failed.
*/
- public CoordinateOperation createOperation(final CoordinateReferenceSystem
sourceCRS,
- final CoordinateReferenceSystem
targetCRS)
+ public List<CoordinateOperation> createOperations(final
CoordinateReferenceSystem sourceCRS,
+ final
CoordinateReferenceSystem targetCRS)
throws FactoryException
{
CoordinateReferenceSystem source = sourceCRS;
@@ -324,24 +328,31 @@ class CoordinateOperationRegistry {
case 3: if (source == sourceCRS || target == targetCRS)
continue;
target = targetCRS;
// 2D → 3D
break;
- default: return null;
+ default: return Collections.emptyList();
}
if (source != null && target != null) try {
- CoordinateOperation operation = search(source, target);
- if (operation != null) {
+ final List<CoordinateOperation> operations = search(source,
target);
+ if (operations != null) {
/*
* Found an operation. If we had to extract the horizontal
part of some 3D CRS, then we
* need to modify the coordinate operation in order to
match the new number of dimensions.
*/
if (combine != 0) {
- operation = propagateVertical(sourceCRS, source !=
sourceCRS,
- targetCRS, target !=
targetCRS, operation);
- if (operation == null) {
- continue;
+ for (int i=operations.size(); --i >= 0;) {
+ CoordinateOperation operation = operations.get(i);
+ operation = propagateVertical(sourceCRS, source !=
sourceCRS,
+ targetCRS, target !=
targetCRS, operation);
+ if (operation != null) {
+ operation = complete(operation, sourceCRS,
targetCRS);
+ operations.set(i, operation);
+ } else {
+ operations.remove(i);
+ }
}
- operation = complete(operation, sourceCRS, targetCRS);
}
- return operation;
+ if (!operations.isEmpty()) {
+ return operations;
+ }
}
} catch (IllegalArgumentException | IncommensurableException e) {
String message =
Resources.format(Resources.Keys.CanNotInstantiateGeodeticObject_1, new
CRSPair(sourceCRS, targetCRS));
@@ -355,7 +366,7 @@ class CoordinateOperationRegistry {
}
/**
- * Returns an operation for conversion or transformation between two
coordinate reference systems.
+ * Returns operations for conversions or transformations between two
coordinate reference systems.
* This method extracts the authority code from the supplied {@code
sourceCRS} and {@code targetCRS},
* and submit them to the {@link #registry}. If no operation is found for
those codes, then this method
* returns {@code null}.
@@ -368,14 +379,15 @@ class CoordinateOperationRegistry {
* @throws IncommensurableException if the units are not compatible or a
unit conversion is non-linear.
* @throws FactoryException if an error occurred while creating the
operation.
*/
- private CoordinateOperation search(final CoordinateReferenceSystem
sourceCRS,
- final CoordinateReferenceSystem
targetCRS)
+ private List<CoordinateOperation> search(final CoordinateReferenceSystem
sourceCRS,
+ final CoordinateReferenceSystem
targetCRS)
throws IllegalArgumentException, IncommensurableException,
FactoryException
{
final List<String> sources = findCode(sourceCRS); if
(sources.isEmpty()) return null;
final List<String> targets = findCode(targetCRS); if
(targets.isEmpty()) return null;
- Collection<CoordinateOperation> operations = null;
- boolean inverse = false;
+ final List<CoordinateOperation> operations = new ArrayList<>();
+ boolean foundDirectOperations = false;
+ boolean useDeprecatedOperations = false;
for (final String sourceID : sources) {
for (final String targetID : targets) {
if (sourceID.equals(targetID)) {
@@ -390,105 +402,82 @@ class CoordinateOperationRegistry {
*/
return null;
}
+ /*
+ * Some pairs of CRS have a lot of coordinate operations
backed by datum shift grids.
+ * We do not want to load all of them until we found the right
coordinate operation.
+ * The non-public Semaphores.METADATA_ONLY mechanism instructs
EPSGDataAccess to
+ * instantiate DeferredCoordinateOperation instead of full
coordinate operations.
+ */
final boolean mdOnly =
Semaphores.queryAndSet(Semaphores.METADATA_ONLY);
try {
+ Collection<CoordinateOperation> authoritatives;
try {
- operations =
registry.createFromCoordinateReferenceSystemCodes(sourceID, targetID);
- inverse = Containers.isNullOrEmpty(operations);
+ authoritatives =
registry.createFromCoordinateReferenceSystemCodes(sourceID, targetID);
+ final boolean inverse =
Containers.isNullOrEmpty(authoritatives);
if (inverse) {
/*
* No operation from 'source' to 'target'
available. But maybe there is an inverse operation.
* This is typically the case when the user wants
to convert from a projected to a geographic CRS.
* The EPSG database usually contains
transformation paths for geographic to projected CRS only.
*/
- operations =
registry.createFromCoordinateReferenceSystemCodes(targetID, sourceID);
- if (Containers.isNullOrEmpty(operations)) {
+ if (foundDirectOperations) {
+ continue; // Ignore
inverse operations if we already have direct ones.
+ }
+ authoritatives =
registry.createFromCoordinateReferenceSystemCodes(targetID, sourceID);
+ if (Containers.isNullOrEmpty(authoritatives)) {
continue;
}
+ } else if (!foundDirectOperations) {
+ foundDirectOperations = true;
+ operations.clear(); // Keep only
direct operations.
}
- } finally {
- if (!mdOnly) {
- Semaphores.clear(Semaphores.METADATA_ONLY);
- }
+ } catch (NoSuchAuthorityCodeException |
MissingFactoryResourceException e) {
+ /*
+ * sourceCode or targetCode is unknown to the
underlying authority factory.
+ * Ignores the exception and fallback on the generic
algorithm provided by
+ * CoordinateOperationFinder.
+ */
+ log(null, e);
+ continue;
}
- } catch (NoSuchAuthorityCodeException |
MissingFactoryResourceException e) {
- /*
- * sourceCode or targetCode is unknown to the underlying
authority factory.
- * Ignores the exception and fallback on the generic
algorithm provided by
- * CoordinateOperationFinder.
- */
- log(null, e);
- continue;
- }
- break; // Stop on the first non-empty set of
operations that we find.
- }
- }
- if (operations == null) {
- return null;
- }
- /*
- * This outer loop is executed exactly once in most case. It may be
executed more than once if an
- * ignoreable error occurred while creating the CoordinateOperation,
in which case we will fallback
- * on the next best choice until we succeed.
- */
- CoordinateOperation bestChoice;
- while (true) {
- /*
- * We will loop over all coordinate operations and select the one
having the largest intersection
- * with the area of interest. Note that if the user did not
specified an area of interest himself,
- * then we need to get one from the CRS. This is necessary for
preventing the transformation from
- * NAD27 to NAD83 in Idaho to select the transform for Alaska
(since the later has a larger area).
- */
- bestChoice = null;
- double largestArea = 0;
- double finestAccuracy = Double.POSITIVE_INFINITY;
- boolean stopAtFirstDeprecated = false;
- for (final Iterator<CoordinateOperation>
it=operations.iterator();;) {
- CoordinateOperation candidate;
- /*
- * Some pair of CRS have a lot of coordinate operations backed
by datum shift grids.
- * We do not want to load all of them until we found the right
coordinate operation.
- * The non-public Semaphores.METADATA_ONLY mechanism instructs
EPSGDataAccess to
- * instantiate DeferredCoordinateOperation instead of full
coordinate operations.
- */
- final boolean mdOnly =
Semaphores.queryAndSet(Semaphores.METADATA_ONLY);
- try {
- try {
- if (!it.hasNext()) break;
- candidate = it.next();
- } finally {
- if (!mdOnly) {
- Semaphores.clear(Semaphores.METADATA_ONLY);
- }
- }
- } catch (BackingStoreException exception) {
- throw exception.unwrapOrRethrow(FactoryException.class);
- }
- if (candidate != null) {
/*
* If we found at least one non-deprecated operation, we
will stop the search at
* the first deprecated one (assuming that deprecated
operations are sorted last).
+ * Deprecated operations are kept only if there is no
non-deprecated operations.
*/
- final boolean isDeprecated = (candidate instanceof
Deprecable) && ((Deprecable) candidate).isDeprecated();
- if (isDeprecated && stopAtFirstDeprecated) {
- break;
- }
- final double area = Extents.area(Extents.intersection(
- Extents.getGeographicBoundingBox(areaOfInterest),
-
Extents.getGeographicBoundingBox(candidate.getDomainOfValidity())));
- if (bestChoice == null || area >= largestArea) {
- final double accuracy =
CRS.getLinearAccuracy(candidate);
- if (bestChoice == null || area != largestArea ||
accuracy < finestAccuracy) {
- bestChoice = candidate;
- if (!Double.isNaN(area)) {
- largestArea = area;
+ try {
+ for (final CoordinateOperation candidate :
authoritatives) {
+ if (candidate != null) {
// Paranoiac check.
+ if ((candidate instanceof Deprecable) &&
((Deprecable) candidate).isDeprecated()) {
+ if (!useDeprecatedOperations &&
!operations.isEmpty()) break;
+ useDeprecatedOperations = true;
+ } else if (useDeprecatedOperations) {
+ useDeprecatedOperations = false;
+ operations.clear(); //
Replace deprecated operations by non-deprecated ones.
+ }
+ operations.add(candidate);
}
- finestAccuracy = Double.isNaN(accuracy) ?
Double.POSITIVE_INFINITY : accuracy;
- stopAtFirstDeprecated = !isDeprecated;
}
+ } catch (BackingStoreException exception) {
+ throw
exception.unwrapOrRethrow(FactoryException.class);
+ }
+ } finally {
+ if (!mdOnly) {
+ Semaphores.clear(Semaphores.METADATA_ONLY);
}
}
}
+ }
+ /*
+ * At this point we got the list of coordinate operations. Now, sort
them in preference order.
+ * We will loop over all coordinate operations and select the one
having the largest intersection
+ * with the area of interest. Note that if the user did not specified
an area of interest himself,
+ * then we need to get one from the CRS. This is necessary for
preventing the transformation from
+ * NAD27 to NAD83 in Idaho to select the transform for Alaska (since
the later has a larger area).
+ */
+ CoordinateOperationSorter.sort(operations,
Extents.getGeographicBoundingBox(areaOfInterest));
+ final ListIterator<CoordinateOperation> it = operations.listIterator();
+ while (it.hasNext()) {
/*
* At this point we filtered a CoordinateOperation by looking only
at its metadata.
* Code following this point will need the full coordinate
operation, including its
@@ -496,45 +485,31 @@ class CoordinateOperationRegistry {
* Conversely, we should not use metadata below this point because
the call to
* inverse(CoordinateOperation) is not guaranteed to preserve all
metadata.
*/
- if (bestChoice == null) break;
- final CoordinateOperation deferred = bestChoice;
+ CoordinateOperation operation = it.next();
try {
- if (bestChoice instanceof DeferredCoordinateOperation) {
- bestChoice = ((DeferredCoordinateOperation)
bestChoice).create();
+ if (operation instanceof DeferredCoordinateOperation) {
+ operation = ((DeferredCoordinateOperation)
operation).create();
}
- if (bestChoice instanceof SingleOperation &&
bestChoice.getMathTransform() == null) {
- bestChoice = fromDefiningConversion((SingleOperation)
bestChoice,
- inverse ? targetCRS :
sourceCRS,
- inverse ? sourceCRS :
targetCRS);
- if (bestChoice == null) {
- return null;
+ if (operation instanceof SingleOperation &&
operation.getMathTransform() == null) {
+ operation = fromDefiningConversion((SingleOperation)
operation,
+ foundDirectOperations ? sourceCRS :
targetCRS,
+ foundDirectOperations ? targetCRS :
sourceCRS);
+ if (operation == null) {
+ it.remove();
+ continue;
}
}
- if (inverse) {
- bestChoice = inverse(bestChoice);
+ if (!foundDirectOperations) {
+ operation = inverse(operation);
}
} catch (NoninvertibleTransformException |
MissingFactoryResourceException e) {
/*
- * If we failed to get the real CoordinateOperation instance,
remove it from the collection
- * and try again in order to get the next best choice. The
Apache SIS implementation allows
- * to remove directly from the Set<CoordinateOperation>, but
that removal may fail with non-SIS
- * implementations, in which case we copy the
CoordinateOperation in a temporary list before
- * removal. We do not perform that copy unconditionally in
order to avoid lazy initialization
- * of unneeded CoordinateOperations like the deprecated ones.
+ * If we failed to get the real CoordinateOperation instance,
remove it from
+ * the collection and try again in order to get the next best
choices.
*/
- boolean removed;
- try {
- removed = operations.remove(deferred);
- } catch (UnsupportedOperationException ignored) {
- operations = new ArrayList<>(operations);
- removed = operations.remove(deferred);
- }
- if (removed) {
- log(null, e);
- continue; // Try again
with the next best case.
- }
- // Should never happen, but if happen anyway we should fail
for avoiding never-ending loop.
- throw (e instanceof FactoryException) ? (FactoryException) e :
new FactoryException(e);
+ log(null, e);
+ it.remove();
+ continue; // Try again with
the next best case.
}
/*
* It is possible that the CRS given to this method were not quite
right. For example the user
@@ -548,10 +523,19 @@ class CoordinateOperationRegistry {
* If we fail here, we are likely to fail for all other
transforms. So we are better to let the
* FactoryException propagate.
*/
- bestChoice = complete(bestChoice, sourceCRS, targetCRS);
- if (filter(bestChoice)) break;
+ operation = complete(operation, sourceCRS, targetCRS);
+ if (filter(operation)) {
+ if (stopAtFirst) {
+ operations.clear();
+ operations.add(operation);
+ break;
+ }
+ it.set(operation);
+ } else {
+ it.remove();
+ }
}
- return bestChoice;
+ return operations;
}
/**
@@ -1204,6 +1188,6 @@ class CoordinateOperationRegistry {
if (exception instanceof NoninvertibleTransformException) {
record.setThrown(exception);
}
- Logging.log(CoordinateOperationFinder.class, "createOperation",
record);
+ Logging.log(CoordinateOperationFinder.class, "createOperations",
record);
}
}
Added:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationSorter.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationSorter.java?rev=1828112&view=auto
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationSorter.java
(added)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationSorter.java
[UTF-8] Sat Mar 31 15:17:48 2018
@@ -0,0 +1,96 @@
+/*
+ * 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;
+
+import java.util.Arrays;
+import java.util.List;
+import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.apache.sis.metadata.iso.extent.Extents;
+import org.apache.sis.referencing.CRS;
+
+
+/**
+ * Used for sorting coordinate operation in preference order.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+final class CoordinateOperationSorter implements
Comparable<CoordinateOperationSorter> {
+ /**
+ * The coordinate operation to sort with other operations.
+ */
+ private final CoordinateOperation operation;
+
+ /**
+ * Intersection between the domain of validity of the operation and the
area of interest,
+ * as a negative value for sorting widest area first. May be {@link
Double#NaN}.
+ */
+ private final double area;
+
+ /**
+ * Accuracy of the coordinate operation.
+ */
+ private final double accuracy;
+
+ /**
+ * Creates a new sorter for the given coordinate operation.
+ *
+ * @param operation the coordinate operation to sort with other
operations.
+ * @param areaOfInterest the geographic area of interest, or {@code null}
if unspecified.
+ */
+ private CoordinateOperationSorter(final CoordinateOperation operation,
final GeographicBoundingBox areaOfInterest) {
+ this.operation = operation;
+ area = -Extents.area(Extents.intersection(areaOfInterest,
+
Extents.getGeographicBoundingBox(operation.getDomainOfValidity())));
+ accuracy = CRS.getLinearAccuracy(operation);
+ }
+
+ /**
+ * Returns -1 if this element should be sorted before the given element.
+ */
+ @Override
+ public int compareTo(final CoordinateOperationSorter other) {
+ int c = Double.compare(area, other.area);
+ if (c == 0) {
+ c = Double.compare(accuracy, other.accuracy);
+ }
+ return c;
+ }
+
+ /**
+ * Sorts in-place the given list of operations.
+ *
+ * @param operations the operation to sort.
+ * @param areaOfInterest the geographic area of interest, or {@code
null} if unspecified.
+ */
+ static void sort(final List<CoordinateOperation> operations, final
GeographicBoundingBox areaOfInterest) {
+ if (operations.size() > 1) {
+ final CoordinateOperationSorter[] s = new
CoordinateOperationSorter[operations.size()];
+ for (int i=0; i<s.length; i++) {
+ s[i] = new CoordinateOperationSorter(operations.get(i),
areaOfInterest);
+ }
+ Arrays.sort(s);
+ operations.clear();
+ for (int i=0; i<s.length; i++) {
+ operations.add(s[i].operation);
+ }
+ }
+ }
+}
Propchange:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationSorter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationSorter.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java?rev=1828112&r1=1828111&r2=1828112&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java
[UTF-8] Sat Mar 31 15:17:48 2018
@@ -91,7 +91,7 @@
* for example by specifying the area of interest.
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.0
* @since 0.6
* @module
*/
Modified:
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java?rev=1828112&r1=1828111&r2=1828112&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationRegistryTest.java
[UTF-8] Sat Mar 31 15:17:48 2018
@@ -66,7 +66,7 @@ import static org.junit.Assume.assumeTru
* The operations are tested with various axis order and dimension in source
and target CRS.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
* @since 0.7
* @module
*/
@@ -150,6 +150,18 @@ public final strictfp class CoordinateOp
}
/**
+ * Gets exactly one coordinate operation from the registry to test.
+ */
+ private CoordinateOperation createOperation(final
CoordinateReferenceSystem sourceCRS,
+ final
CoordinateReferenceSystem targetCRS) throws FactoryException
+ {
+ registry.stopAtFirst = true;
+ final List<CoordinateOperation> operations =
registry.createOperations(sourceCRS, targetCRS);
+ assertEquals("Invalid number of operations.", 1, operations.size());
+ return operations.get(0);
+ }
+
+ /**
* Tests <cite>"NTF (Paris) to WGS 84 (1)"</cite> operation with source
and target CRS conform to EPSG definitions.
*
* @throws ParseException if a CRS used in this test can not be parsed.
@@ -169,7 +181,7 @@ public final strictfp class CoordinateOp
// Intentionally omit Id[“EPSG”, 4807] for testing capability
to find it back.
final CoordinateReferenceSystem targetCRS =
CommonCRS.WGS84.geographic();
- final CoordinateOperation operation =
registry.createOperation(sourceCRS, targetCRS);
+ final CoordinateOperation operation = createOperation(sourceCRS,
targetCRS);
verifyNTF(operation, "geog2D domain", true);
/*
* Same test point than the one used in
FranceGeocentricInterpolationTest:
@@ -207,7 +219,7 @@ public final strictfp class CoordinateOp
" Unit[“degree”, 0.017453292519943295]]");
final CoordinateReferenceSystem targetCRS =
CommonCRS.WGS84.normalizedGeographic();
- final CoordinateOperation operation =
registry.createOperation(sourceCRS, targetCRS);
+ final CoordinateOperation operation = createOperation(sourceCRS,
targetCRS);
verifyNTF(operation, "geog2D domain", false);
transform = operation.getMathTransform();
@@ -238,7 +250,7 @@ public final strictfp class CoordinateOp
" Unit[“grad”, 0.015707963267948967]]");
final CoordinateReferenceSystem sourceCRS =
CommonCRS.WGS84.normalizedGeographic();
- final CoordinateOperation operation =
registry.createOperation(sourceCRS, targetCRS);
+ final CoordinateOperation operation = createOperation(sourceCRS,
targetCRS);
transform = operation.getMathTransform();
tolerance = Formulas.ANGULAR_TOLERANCE;
@@ -270,7 +282,7 @@ public final strictfp class CoordinateOp
" Axis[“Height (h)”, UP, Unit[“m”, 1]]]");
final CoordinateReferenceSystem targetCRS =
CommonCRS.WGS84.geographic3D();
- final CoordinateOperation operation =
registry.createOperation(sourceCRS, targetCRS);
+ final CoordinateOperation operation = createOperation(sourceCRS,
targetCRS);
verifyNTF(operation, "geog3D domain", false);
transform = operation.getMathTransform();
@@ -305,7 +317,7 @@ public final strictfp class CoordinateOp
final CoordinateReferenceSystem targetCRS =
DefaultGeographicCRS.castOrCopy(CommonCRS.WGS84.geographic3D()).forConvention(AxesConvention.NORMALIZED);
- final CoordinateOperation operation =
registry.createOperation(sourceCRS, targetCRS);
+ final CoordinateOperation operation = createOperation(sourceCRS,
targetCRS);
verifyNTF(operation, "geog3D domain", false);
transform = operation.getMathTransform();
@@ -384,14 +396,14 @@ public final strictfp class CoordinateOp
public void testFindDespiteDifferentAxisOrder() throws FactoryException {
CoordinateReferenceSystem sourceCRS =
crsFactory.createGeographicCRS("EPSG:4625");
CoordinateReferenceSystem targetCRS =
crsFactory.createGeographicCRS("EPSG:5489");
- CoordinateOperation operation = registry.createOperation(sourceCRS,
targetCRS);
+ CoordinateOperation operation = createOperation(sourceCRS, targetCRS);
assertEpsgNameAndIdentifierEqual("Martinique 1938 to RGAF09 (1)",
5491, operation);
/*
* Above was only a verification using the source and target CRS
expected by EPSG dataset.
* Now the interesting test: use a target CRS with different axis
order.
*/
targetCRS = crsFactory.createGeographicCRS("EPSG:7086");
- operation = registry.createOperation(sourceCRS, targetCRS);
+ operation = createOperation(sourceCRS, targetCRS);
assertEpsgNameWithoutIdentifierEqual("Martinique 1938 to RGAF09 (1)",
operation);
final ParameterValueGroup p = ((SingleOperation)
operation).getParameterValues();
/*