Author: desruisseaux
Date: Fri Apr 8 21:48:58 2016
New Revision: 1738311
URL: http://svn.apache.org/viewvc?rev=1738311&view=rev
Log:
Partial port of CoordinateOperationRegistry for finding coordinate operation by
the "late-binding" approach.
Added:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
- copied, changed from r1738271,
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
(with props)
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java?rev=1738311&r1=1738310&r2=1738311&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
[UTF-8] Fri Apr 8 21:48:58 2016
@@ -341,7 +341,7 @@ public class IdentifiedObjectFinder {
* If the set returned by {@link #find(IdentifiedObject)} contains exactly
one element,
* then that element is returned. Otherwise this method returns {@code
null}.
*
- * <p>Note that this method returns {@code null} even if there is more
than one element,
+ * <p>This method returns {@code null} if there is more than one element
* because in such case we consider that there is an ambiguity.</p>
*
* @param object The object looked up.
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java?rev=1738311&r1=1738310&r2=1738311&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
[UTF-8] Fri Apr 8 21:48:58 2016
@@ -515,7 +515,7 @@ check: for (int isTarget=0; ; isTar
/**
* Returns the interpolation CRS of the given coordinate operation, or
{@code null} if none.
*/
- private static CoordinateReferenceSystem getInterpolationCRS(final
CoordinateOperation operation) {
+ static CoordinateReferenceSystem getInterpolationCRS(final
CoordinateOperation operation) {
return (operation instanceof AbstractCoordinateOperation)
? ((AbstractCoordinateOperation)
operation).getInterpolationCRS() : null;
}
Copied:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
(from r1738271,
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java)
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java?p2=sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java&p1=sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java&r1=1738271&r2=1738311&rev=1738311&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java
[UTF-8] Fri Apr 8 21:48:58 2016
@@ -18,66 +18,43 @@ package org.apache.sis.referencing.opera
import java.util.Map;
import java.util.HashMap;
-import java.util.List;
-import java.util.Collections;
-import javax.measure.unit.Unit;
-import javax.measure.quantity.Duration;
-import javax.measure.converter.ConversionException;
import org.opengis.util.FactoryException;
import org.opengis.referencing.IdentifiedObject;
-import org.opengis.referencing.cs.*;
-import org.opengis.referencing.crs.*;
-import org.opengis.referencing.datum.*;
-import org.opengis.referencing.operation.*;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.extent.Extent;
import org.opengis.metadata.extent.GeographicBoundingBox;
import org.opengis.metadata.quality.PositionalAccuracy;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.parameter.ParameterDescriptorGroup;
-import org.apache.sis.internal.metadata.AxisDirections;
-import org.apache.sis.internal.metadata.VerticalDatumTypes;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.Conversion;
+import org.opengis.referencing.operation.Transformation;
+import org.opengis.referencing.operation.SingleOperation;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.referencing.operation.CoordinateOperationFactory;
+import org.opengis.referencing.operation.NoninvertibleTransformException;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.Matrix;
import org.apache.sis.internal.metadata.ReferencingServices;
-import org.apache.sis.internal.referencing.ReferencingUtilities;
import org.apache.sis.internal.referencing.CoordinateOperations;
import org.apache.sis.internal.referencing.PositionalAccuracyConstant;
-import org.apache.sis.internal.referencing.provider.Geographic2Dto3D;
-import org.apache.sis.internal.referencing.provider.Geographic3Dto2D;
-import org.apache.sis.internal.referencing.provider.GeographicToGeocentric;
-import org.apache.sis.internal.referencing.provider.GeocentricToGeographic;
-import org.apache.sis.internal.referencing.provider.GeocentricAffine;
import org.apache.sis.internal.referencing.provider.Affine;
-import org.apache.sis.internal.util.Constants;
-import org.apache.sis.measure.Units;
import org.apache.sis.metadata.iso.citation.Citations;
-import org.apache.sis.metadata.iso.extent.Extents;
-import org.apache.sis.parameter.TensorParameters;
-import org.apache.sis.referencing.CRS;
-import org.apache.sis.referencing.CommonCRS;
-import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.referencing.NamedIdentifier;
-import org.apache.sis.referencing.cs.CoordinateSystems;
import org.apache.sis.referencing.datum.BursaWolfParameters;
-import org.apache.sis.referencing.datum.DefaultGeodeticDatum;
import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.transform.MathTransforms;
-import
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.resources.Vocabulary;
-import static org.apache.sis.util.Utilities.equalsIgnoreMetadata;
-
// Branch-dependent imports
import java.util.Objects;
/**
- * Infers a conversion of transformation path from a source CRS to a target
CRS.
- * Instances of this class are created by {@link
DefaultCoordinateOperationFactory}.
- * The only public method is {@link
#createOperation(CoordinateReferenceSystem, CoordinateReferenceSystem)},
- * which dispatches its work to the {@code createOperationStep(…)} protected
methods.
- * Developers can override those protected methods if they want to alter the
way some operations are created.
+ * Base class of code that search for coordinate operation, either by looking
in a registry maintained by an authority
+ * or by trying to infer the coordinate operation by itself.
*
* <div class="section">Limitations</div>
* <ul>
@@ -89,21 +66,8 @@ import java.util.Objects;
* @since 0.7
* @version 0.7
* @module
- *
- * @see
DefaultCoordinateOperationFactory#createOperation(CoordinateReferenceSystem,
CoordinateReferenceSystem, CoordinateOperationContext)
*/
-public class CoordinateOperationInference {
- /**
- * The accuracy threshold (in metres) for allowing the use of Molodensky
approximation instead than the
- * Geocentric Translation method. The accuracy of datum shifts with
Molodensky approximation is about 5
- * or 10 metres. However for this constant, we are not interested in
absolute accuracy but rather in the
- * difference between Molodensky and Geocentric Translation methods, which
is much lower. We nevertheless
- * use a relatively high threshold as a conservative approach.
- *
- * @see #desiredAccuracy
- */
- private static final double MOLODENSKY_ACCURACY = 5;
-
+class CoordinateOperationFinder {
/**
* The identifier for an identity operation.
*/
@@ -112,7 +76,7 @@ public class CoordinateOperationInferenc
/**
* The identifier for conversion using an affine transform for axis
swapping and/or unit conversions.
*/
- private static final Identifier AXIS_CHANGES =
createIdentifier(Vocabulary.Keys.AxisChanges);
+ static final Identifier AXIS_CHANGES =
createIdentifier(Vocabulary.Keys.AxisChanges);
/**
* The identifier for a transformation which is a datum shift without
{@link BursaWolfParameters}.
@@ -121,24 +85,24 @@ public class CoordinateOperationInferenc
*
* @see
org.apache.sis.internal.referencing.PositionalAccuracyConstan#DATUM_SHIFT_OMITTED
*/
- private static final Identifier ELLIPSOID_CHANGE =
createIdentifier(Vocabulary.Keys.EllipsoidChange);
+ static final Identifier ELLIPSOID_CHANGE =
createIdentifier(Vocabulary.Keys.EllipsoidChange);
/**
* The identifier for a transformation which is a datum shift.
*
* @see
org.apache.sis.internal.referencing.PositionalAccuracyConstant#DATUM_SHIFT_APPLIED
*/
- private static final Identifier DATUM_SHIFT =
createIdentifier(Vocabulary.Keys.DatumShift);
+ static final Identifier DATUM_SHIFT =
createIdentifier(Vocabulary.Keys.DatumShift);
/**
* The identifier for a geocentric conversion.
*/
- private static final Identifier GEOCENTRIC_CONVERSION =
createIdentifier(Vocabulary.Keys.GeocentricConversion);
+ static final Identifier GEOCENTRIC_CONVERSION =
createIdentifier(Vocabulary.Keys.GeocentricConversion);
/**
* The identifier for an inverse operation.
*/
- private static final Identifier INVERSE_OPERATION =
createIdentifier(Vocabulary.Keys.InverseOperation);
+ static final Identifier INVERSE_OPERATION =
createIdentifier(Vocabulary.Keys.InverseOperation);
/**
* Creates an identifier in the Apache SIS namespace for the given
vocabulary key.
@@ -150,50 +114,29 @@ public class CoordinateOperationInferenc
/**
* The factory to use for creating coordinate operations.
*/
- private final CoordinateOperationFactory factory;
+ final CoordinateOperationFactory factory;
/**
* Used only when we need a SIS-specific method.
*/
- private final DefaultCoordinateOperationFactory factorySIS;
+ final DefaultCoordinateOperationFactory factorySIS;
/**
* The spatio-temporal area of interest, or {@code null} if none.
*/
- private Extent areaOfInterest;
+ Extent areaOfInterest;
/**
* The geographic component of the area of interest, or {@code null} if
none.
*/
- private GeographicBoundingBox bbox;
+ GeographicBoundingBox bbox;
/**
* The desired accuracy in metres, or 0 for the best accuracy available.
*
* @see #MOLODENSKY_ACCURACY
*/
- private double desiredAccuracy;
-
- /**
- * Identifiers used as the basis for identifier of CRS used as an
intermediate step.
- * The values can be of two kinds:
- *
- * <ul>
- * <li>If the value is an instance of {@link Integer}, then this is the
number
- * of identifiers derived from the identifier associated to the
key.</li>
- * <li>Otherwise the key is itself an {@link Indentifier} derived from
another
- * identifier, and the value is that identifier.</li>
- * </ul>
- *
- * @see #derivedFrom(IdentifiedObject)
- */
- private final Map<Identifier,Object> identifierOfStepCRS;
-
- /**
- * The pair of source and target CRS for which we already searched a
coordinate operation.
- * This is used as a safety against infinite recursivity.
- */
- private final Map<CRSPair,Boolean> previousSearches;
+ double desiredAccuracy;
/**
* Creates a new instance for the given factory and context.
@@ -201,8 +144,8 @@ public class CoordinateOperationInferenc
* @param factory The factory to use for creating coordinate operations.
* @param context The area of interest and desired accuracy, or {@code
null} if none.
*/
- public CoordinateOperationInference(final CoordinateOperationFactory
factory,
- final CoordinateOperationContext
context)
+ public CoordinateOperationFinder(final CoordinateOperationFactory factory,
+ final CoordinateOperationContext context)
{
ArgumentChecks.ensureNonNull("factory", factory);
this.factory = factory;
@@ -213,698 +156,6 @@ public class CoordinateOperationInferenc
desiredAccuracy = context.getDesiredAccuracy();
bbox = context.getGeographicBoundingBox();
}
- identifierOfStepCRS = new HashMap<>(8);
- previousSearches = new HashMap<>(8);
- }
-
- /**
- * Infers an operation for conversion or transformation between two
coordinate reference systems.
- * This method inspects the given CRS and delegates the work to one or
many {@code createOperationStep(…)} methods.
- * Note that some {@code createOperationStep(…)} methods may callback this
{@code createOperation(…)} method with
- * another source or target CRS.
- *
- * @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
- {
- ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS);
- ArgumentChecks.ensureNonNull("targetCRS", targetCRS);
- if (!previousSearches.isEmpty()) {
- // TODO: verify here if the path is defined in EPSG database.
- }
- final CRSPair key = new CRSPair(sourceCRS, targetCRS);
- if (previousSearches.put(key, Boolean.TRUE) != null) {
- throw new
FactoryException(Errors.format(Errors.Keys.RecursiveCreateCallForCode_2,
CoordinateOperation.class, key));
- }
- if (bbox == null) {
- bbox =
Extents.intersection(CRS.getGeographicBoundingBox(sourceCRS),
-
CRS.getGeographicBoundingBox(targetCRS));
- areaOfInterest =
CoordinateOperationContext.setGeographicBoundingBox(areaOfInterest, bbox);
- }
-
////////////////////////////////////////////////////////////////////////////////
- ////
////
- //// Derived → any Single CRS
////
- ////
////
-
////////////////////////////////////////////////////////////////////////////////
- if (sourceCRS instanceof GeneralDerivedCRS) {
- final GeneralDerivedCRS source = (GeneralDerivedCRS) sourceCRS;
- if (targetCRS instanceof GeneralDerivedCRS) {
- return createOperationStep(source, (GeneralDerivedCRS)
targetCRS);
- }
- if (targetCRS instanceof SingleCRS) {
- return createOperationStep(source, (SingleCRS) targetCRS);
- }
- }
-
////////////////////////////////////////////////////////////////////////////////
- ////
////
- //// any Single CRS → Derived
////
- ////
////
-
////////////////////////////////////////////////////////////////////////////////
- if (targetCRS instanceof GeneralDerivedCRS) {
- final GeneralDerivedCRS target = (GeneralDerivedCRS) targetCRS;
- if (sourceCRS instanceof SingleCRS) {
- return createOperationStep((SingleCRS) sourceCRS, target);
- }
- }
-
////////////////////////////////////////////////////////////////////////////////
- ////
////
- //// Geodetic → Geocetric, Geographic or Projected
////
- ////
////
-
////////////////////////////////////////////////////////////////////////////////
- if (sourceCRS instanceof GeodeticCRS) {
- final GeodeticCRS source = (GeodeticCRS) sourceCRS;
- if (targetCRS instanceof GeodeticCRS) {
- return createOperationStep(source, (GeodeticCRS) targetCRS);
- }
- if (targetCRS instanceof VerticalCRS) {
- return createOperationStep(source, (VerticalCRS) targetCRS);
- }
- }
-
////////////////////////////////////////////////////////////////////////////////
- ////
////
- //// Vertical → Vertical
////
- ////
////
-
////////////////////////////////////////////////////////////////////////////////
- if (sourceCRS instanceof VerticalCRS) {
- final VerticalCRS source = (VerticalCRS) sourceCRS;
- if (targetCRS instanceof VerticalCRS) {
- return createOperationStep(source, (VerticalCRS) targetCRS);
- }
- }
-
////////////////////////////////////////////////////////////////////////////////
- ////
////
- //// Temporal → Temporal
////
- ////
////
-
////////////////////////////////////////////////////////////////////////////////
- if (sourceCRS instanceof TemporalCRS) {
- final TemporalCRS source = (TemporalCRS) sourceCRS;
- if (targetCRS instanceof TemporalCRS) {
- return createOperationStep(source, (TemporalCRS) targetCRS);
- }
- }
-
////////////////////////////////////////////////////////////////////////////////
- ////
////
- //// Compound ↔ various CRS
////
- ////
////
-
////////////////////////////////////////////////////////////////////////////////
- if (sourceCRS instanceof CompoundCRS || targetCRS instanceof
CompoundCRS) {
- return createOperationStep(sourceCRS,
CRS.getSingleComponents(sourceCRS),
- targetCRS,
CRS.getSingleComponents(targetCRS));
- }
- throw new OperationNotFoundException(notFoundMessage(sourceCRS,
targetCRS));
- }
-
- /**
- * Creates an operation 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}.
- *
- * <p>The default implementation constructs the following operation
chain:</p>
- * <blockquote><code>sourceCRS → {@linkplain
GeneralDerivedCRS#getBaseCRS() baseCRS} → targetCRS</code></blockquote>
- *
- * where the conversion from {@code baseCRS} to {@code targetCRS} is
obtained from
- * <code>targetCRS.{@linkplain GeneralDerivedCRS#getConversionFromBase()
getConversionFromBase()}</code>.
- *
- * @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 SingleCRS
sourceCRS,
- final GeneralDerivedCRS
targetCRS)
- throws FactoryException
- {
- final CoordinateOperation step1 = createOperation(sourceCRS,
targetCRS.getBaseCRS());
- final CoordinateOperation step2 = targetCRS.getConversionFromBase();
- return concatenate(step1, step2);
- }
-
- /**
- * Creates an operation from a derived CRS to an arbitrary single
coordinate reference system.
- * Conversions from {@code ProjectedCRS} to {@code GeographicCRS} are also
handled by this method,
- * since projected CRS are a special kind of {@code GeneralDerivedCRS}.
- *
- * <p>The default implementation constructs the following operation
chain:</p>
- * <blockquote><code>sourceCRS → {@linkplain
GeneralDerivedCRS#getBaseCRS() baseCRS} → targetCRS</code></blockquote>
- *
- * where the conversion from {@code sourceCRS} to {@code baseCRS} is
obtained from the inverse of
- * <code>sourceCRS.{@linkplain GeneralDerivedCRS#getConversionFromBase()
getConversionFromBase()}</code>.
- *
- * @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)
- 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 (NoninvertibleTransformException exception) {
- throw new OperationNotFoundException(notFoundMessage(sourceCRS,
targetCRS), exception);
- }
- return concatenate(step1, step2);
- }
-
- /**
- * Creates an operation between two derived coordinate reference systems.
- * The default implementation performs three steps:
- *
- * <ol>
- * <li>Convert from {@code sourceCRS} to its base CRS.</li>
- * <li>Convert the source base CRS to target base CRS.</li>
- * <li>Convert from the target base CRS to the {@code targetCRS}.</li>
- * </ol>
- *
- * @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)
- 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 (NoninvertibleTransformException exception) {
- throw new OperationNotFoundException(notFoundMessage(sourceCRS,
targetCRS), exception);
- }
- return concatenate(step1, step2, step3);
- }
-
- /**
- * Creates an operation between two geodetic (geographic or geocentric)
coordinate reference systems.
- * The default implementation can:
- *
- * <ul>
- * <li>adjust axis order and orientation, for example converting from
(<cite>North</cite>, <cite>West</cite>)
- * axes to (<cite>East</cite>, <cite>North</cite>) axes,</li>
- * <li>apply units conversion if needed,</li>
- * <li>perform longitude rotation if needed,</li>
- * <li>perform datum shift if {@linkplain BursaWolfParameters Bursa-Wolf
parameters} are available
- * for the area of interest.</li>
- * </ul>
- *
- * @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)
- throws FactoryException
- {
- final GeodeticDatum sourceDatum = sourceCRS.getDatum();
- final GeodeticDatum targetDatum = targetCRS.getDatum();
- Matrix datumShift = null;
- /*
- * If the prime meridian is not the same, we will concatenate a
longitude rotation before or after datum shift
- * (that concatenation will be performed by the customized
DefaultMathTransformFactory.Context created below).
- * Actually we do not know if the longitude rotation should be before
or after datum shift. But this ambiguity
- * can usually be ignored because Bursa-Wolf parameters are always
used with source and target prime meridians
- * set to Greenwich in EPSG dataset 8.9. For safety, the SIS's
DefaultGeodeticDatum class ensures that if the
- * prime meridian are not the same, then the target meridian must be
Greenwich.
- */
- final DefaultMathTransformFactory.Context context =
ReferencingUtilities.createTransformContext(
- sourceCRS, targetCRS, new MathTransformContext(sourceDatum,
targetDatum));
- /*
- * If both CRS use the same datum and the same prime meridian, then
the coordinate operation is only axis
- * swapping, unit conversion or change of coordinate system type
(Ellipsoidal ↔ Cartesian ↔ Spherical).
- * Otherwise (if the datum are not the same), we will need to perform
a scale, translation and rotation
- * in Cartesian space using the Bursa-Wolf parameters. If the user
does not require the best accuracy,
- * then the Molodensky approximation may be used for avoiding the
conversion step to geocentric CRS.
- */
- Identifier identifier;
- boolean isGeographicToGeocentric = false;
- final CoordinateSystem sourceCS = context.getSourceCS();
- final CoordinateSystem targetCS = context.getTargetCS();
- if (equalsIgnoreMetadata(sourceDatum, targetDatum)) {
- final boolean isGeocentricToGeographic;
- isGeographicToGeocentric = (sourceCS instanceof EllipsoidalCS &&
targetCS instanceof CartesianCS);
- isGeocentricToGeographic = (sourceCS instanceof CartesianCS &&
targetCS instanceof EllipsoidalCS);
- /*
- * Above booleans should never be true in same time. If it
nevertheless happen (we are paranoiac;
- * maybe a lazy user implemented all interfaces in a single
class), do not apply any geographic ↔
- * geocentric conversion. Instead do as if the coordinate system
types were the same.
- */
- if (isGeocentricToGeographic ^ isGeographicToGeocentric) {
- identifier = GEOCENTRIC_CONVERSION;
- } else {
- identifier = AXIS_CHANGES;
- }
- } else {
- identifier = ELLIPSOID_CHANGE;
- if (sourceDatum instanceof DefaultGeodeticDatum) {
- datumShift = ((DefaultGeodeticDatum)
sourceDatum).getPositionVectorTransformation(targetDatum, areaOfInterest);
- if (datumShift != null) {
- identifier = DATUM_SHIFT;
- }
- }
- }
- /*
- * Conceptually, all transformations below could done by first
converting from the source coordinate
- * system to geocentric Cartesian coordinates (X,Y,Z), apply an affine
transform represented by the
- * datum shift matrix, then convert from the (X′,Y′,Z′) coordinates to
the target coordinate system.
- * However there is two exceptions to this path:
- *
- * 1) In the particular where both the source and target CS are
ellipsoidal, we may use the
- * Molodensky approximation as a shortcut (if the desired
accuracy allows).
- *
- * 2) Even if we really go through the XYZ coordinates without
Molodensky approximation, there is
- * at least 9 different ways to name this operation depending on
whether the source and target
- * CRS are geocentric or geographic, 2- or 3-dimensional, whether
there is a translation or not,
- * the rotation sign, etc. We try to use the most specific name
if we can find one, and fallback
- * on an arbitrary name only in last resort.
- */
- final DefaultMathTransformFactory mtFactory =
factorySIS.getDefaultMathTransformFactory();
- MathTransform before = null, after = null;
- ParameterValueGroup parameters;
- if (datumShift != null) {
- /*
- * If the transform can be represented by a single coordinate
operation, returns that operation.
- * Possible operations are:
- *
- * - Geocentric translation (in geocentric,
geographic-2D or geographic-3D domains)
- * - Position Vector transformation (in geocentric,
geographic-2D or geographic-3D domains)
- *
- * Otherwise, maybe we failed to create the operation because the
coordinate system type were not the same.
- * Convert unconditionally to XYZ geocentric coordinates and apply
the datum shift in that coordinate space.
- */
- parameters = GeocentricAffine.createParameters(sourceCS, targetCS,
datumShift, desiredAccuracy >= MOLODENSKY_ACCURACY);
- if (parameters == null) {
- parameters =
TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE),
datumShift);
- final CoordinateSystem normalized =
CommonCRS.WGS84.geocentric().getCoordinateSystem();
- before = mtFactory.createCoordinateSystemChange(sourceCS,
normalized);
- after = mtFactory.createCoordinateSystemChange(normalized,
targetCS);
- context.setSource(normalized);
- context.setTarget(normalized);
- }
- } else if (identifier == GEOCENTRIC_CONVERSION) {
- parameters = (isGeographicToGeocentric ?
GeographicToGeocentric.PARAMETERS
- :
GeocentricToGeographic.PARAMETERS).createValue();
- } else {
- final int sourceDim = sourceCS.getDimension();
- final int targetDim = targetCS.getDimension();
- if ((sourceDim & ~1) == 2 && (sourceDim ^ targetDim) == 1 //
sourceDim == 2 or 3 and difference with targetDim is 1.
- && (sourceCS instanceof EllipsoidalCS)
- && (targetCS instanceof EllipsoidalCS))
- {
- parameters = (sourceDim == 2 ? Geographic2Dto3D.PARAMETERS
- :
Geographic3Dto2D.PARAMETERS).createValue();
- } else {
- parameters =
TensorParameters.WKT1.createValueGroup(properties(Constants.AFFINE)); //
Initialized to identity.
- parameters.parameter(Constants.NUM_COL).setValue(targetDim +
1);
- parameters.parameter(Constants.NUM_ROW).setValue(targetDim +
1);
- before = mtFactory.createCoordinateSystemChange(sourceCS,
targetCS);
- context.setSource(targetCS);
- }
- }
- /*
- * Transform between differents datums using Bursa Wolf parameters.
The Bursa Wolf parameters are used
- * with "standard" geocentric CS, i.e. with X axis towards the prime
meridian, Y axis towards East and
- * Z axis toward North, unless the Molodensky approximation is used.
The following steps are applied:
- *
- * source CRS →
- * normalized CRS with source datum →
- * normalized CRS with target datum →
- * target CRS
- *
- * Those steps may be either explicit with the 'before' and 'after'
transform, or implicit with the
- * Context parameter.
- */
- MathTransform transform =
mtFactory.createParameterizedTransform(parameters, context);
- final OperationMethod method = mtFactory.getLastMethodUsed();
- if (before != null) {
- transform = mtFactory.createConcatenatedTransform(before,
transform);
- if (after != null) {
- transform = mtFactory.createConcatenatedTransform(transform,
after);
- }
- }
- return createFromMathTransform(properties(identifier), sourceCRS,
targetCRS, transform, method, parameters, null);
- }
-
- /**
- * Creates an operation between a geodetic and a vertical coordinate
reference systems.
- * The height returned by this method will usually be part of a
- * {@linkplain DefaultPassThroughOperation pass-through operation}.
- *
- * @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)
- throws FactoryException
- {
- /*
- * We will perform the conversion or transformation as a 3 steps
process:
- *
- * source CRS →
- * interpolation CRS →
- * ellipsoidal height →
- * target height
- */
- CoordinateOperation step1 = null;
- CoordinateOperation step2;
- CoordinateOperation step3 = null;
- /*
- * Convert the source CRS to the CRS needed for transforming the
heights.
- * For now this step is fixed to a three-dimensional geographic CRS,
but
- * a future version should use a plugin-mechanism, with the code below
- * as the last fallback.
- */
- CoordinateReferenceSystem interpolationCRS = sourceCRS;
- CoordinateSystem interpolationCS =
interpolationCRS.getCoordinateSystem();
- if (!(interpolationCS instanceof EllipsoidalCS)) {
- final EllipsoidalCS cs =
CommonCRS.WGS84.geographic3D().getCoordinateSystem();
- if (!equalsIgnoreMetadata(interpolationCS, cs)) {
- step1 = createOperation(sourceCRS, factorySIS.getCRSFactory()
- .createGeographicCRS(derivedFrom(sourceCRS),
sourceCRS.getDatum(), cs));
- interpolationCRS = step1.getTargetCRS();
- interpolationCS = interpolationCRS.getCoordinateSystem();
- }
- }
- /*
- * Transform from ellipsoidal height to the height requested by the
caller.
- * This operation requires the horizontal components (φ,λ) of source
CRS,
- * unless the user asked for ellipsooidal height (which strictly
speaking
- * is not allowed by ISO 19111). Those horizontal components are given
by
- * the interpolation CRS.
- *
- * TODO: store the interpolationCRS in some field for allowing other
methods to use it.
- */
- final int i = AxisDirections.indexOfColinear(interpolationCS,
AxisDirection.UP);
- if (i < 0) {
- throw new OperationNotFoundException(notFoundMessage(sourceCRS,
targetCRS));
- }
- final CoordinateSystemAxis expectedAxis = interpolationCS.getAxis(i);
- final boolean isEllipsoidalHeight; // Whether heightCRS is okay
or need to be recreated.
- VerticalCRS heightCRS = targetCRS; // First candidate, will be
replaced if it doesn't fit.
- VerticalCS heightCS = heightCRS.getCoordinateSystem();
- if (equalsIgnoreMetadata(heightCS.getAxis(0), expectedAxis)) {
- isEllipsoidalHeight =
VerticalDatumTypes.ELLIPSOIDAL.equals(heightCRS.getDatum().getVerticalDatumType());
- } else {
- heightCRS = CommonCRS.Vertical.ELLIPSOIDAL.crs();
- heightCS = heightCRS.getCoordinateSystem();
- isEllipsoidalHeight = equalsIgnoreMetadata(heightCS.getAxis(0),
expectedAxis);
- if (!isEllipsoidalHeight) {
- heightCS =
factorySIS.getCSFactory().createVerticalCS(derivedFrom(heightCS), expectedAxis);
- }
- }
- if (!isEllipsoidalHeight) { // 'false' if we need
to change datum, unit or axis direction.
- heightCRS = factorySIS.getCRSFactory()
- .createVerticalCRS(derivedFrom(heightCRS),
CommonCRS.Vertical.ELLIPSOIDAL.datum(), heightCS);
- }
- if (heightCRS != targetCRS) {
- step3 = createOperation(heightCRS, targetCRS); // May need
interpolationCRS for performing datum change.
- heightCRS = (VerticalCRS) step3.getSourceCRS();
- heightCS = heightCRS.getCoordinateSystem();
- }
- /*
- * Conversion from three-dimensional geographic CRS to ellipsoidal
height.
- * This part does nothing more than dropping the horizontal components,
- * like the "Geographic3D to 2D conversion" (EPSG:9659).
- * It is not the job of this block to perform unit conversions.
- * Unit conversions, if needed, are done by 'step3' computed in above
block.
- *
- * The "Geographic3DtoVertical.txt" file in the provider package is a
reminder.
- * If this policy is changed, that file should be edited accordingly.
- */
- final int srcDim = interpolationCS.getDimension();
// Should always be 3.
- final int tgtDim = heightCS.getDimension();
// Should always be 1.
- final Matrix matrix = Matrices.createZero(tgtDim + 1, srcDim + 1);
- 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);
- }
-
- /**
- * Creates an operation between two vertical coordinate reference systems.
- * The default implementation checks if both CRS use the same datum, then
- * adjusts for axis direction and units.
- *
- * @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.
- *
- * @todo Needs to implement vertical datum shift.
- */
- protected CoordinateOperation createOperationStep(final VerticalCRS
sourceCRS,
- final VerticalCRS
targetCRS)
- throws FactoryException
- {
- final VerticalDatum sourceDatum = sourceCRS.getDatum();
- final VerticalDatum targetDatum = targetCRS.getDatum();
- if (!equalsIgnoreMetadata(sourceDatum, targetDatum)) {
- throw new OperationNotFoundException(notFoundMessage(sourceDatum,
targetDatum));
- }
- final VerticalCS sourceCS = sourceCRS.getCoordinateSystem();
- final VerticalCS targetCS = targetCRS.getCoordinateSystem();
- final Matrix matrix;
- try {
- matrix = CoordinateSystems.swapAndScaleAxes(sourceCS, targetCS);
- } catch (IllegalArgumentException | ConversionException exception) {
- throw new OperationNotFoundException(notFoundMessage(sourceCRS,
targetCRS), exception);
- }
- return createFromAffineTransform(AXIS_CHANGES, sourceCRS, targetCRS,
matrix);
- }
-
- /**
- * Creates an operation between two temporal coordinate reference systems.
- * The default implementation checks if both CRS use the same datum, then
- * adjusts for axis direction, units and epoch.
- *
- * @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)
- throws FactoryException
- {
- final TemporalDatum sourceDatum = sourceCRS.getDatum();
- final TemporalDatum targetDatum = targetCRS.getDatum();
- final TimeCS sourceCS = sourceCRS.getCoordinateSystem();
- final TimeCS targetCS = targetCRS.getCoordinateSystem();
- /*
- * Compute the epoch shift. The epoch is the time "0" in a particular
coordinate reference system.
- * For example, the epoch for java.util.Date object is january 1, 1970
at 00:00 UTC. We compute how
- * much to add to a time in 'sourceCRS' in order to get a time in
'targetCRS'. This "epoch shift" is
- * in units of 'targetCRS'.
- */
- final Unit<Duration> targetUnit =
targetCS.getAxis(0).getUnit().asType(Duration.class);
- double epochShift = sourceDatum.getOrigin().getTime() -
- targetDatum.getOrigin().getTime();
- epochShift =
Units.MILLISECOND.getConverterTo(targetUnit).convert(epochShift);
- /*
- * Check axis directions. The method 'swapAndScaleAxes' should returns
a matrix of size 2×2.
- * The element at index (0,0) may be +1 if source and target axes are
in the same direction,
- * or -1 if there are in opposite direction ("PAST" vs "FUTURE"). The
value may be something
- * else than ±1 if a unit conversion is applied too. For example the
value is 60 if time in
- * sourceCRS is in hours while time in targetCRS is in minutes.
- *
- * The "epoch shift" previously computed is a translation.
Consequently, it is added to element (0,1).
- */
- final Matrix matrix;
- try {
- matrix = CoordinateSystems.swapAndScaleAxes(sourceCS, targetCS);
- } catch (IllegalArgumentException | ConversionException exception) {
- throw new OperationNotFoundException(notFoundMessage(sourceCRS,
targetCRS), exception);
- }
- 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);
- }
-
- /**
- * Creates an operation between at least one {@code CompoundCRS} (usually
the source) and an arbitrary CRS.
- * The default implementation tries to invoke the {@link #createOperation
createOperation(…)} method with
- * 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>).
- *
- * @param sourceCRS input coordinate reference system.
- * @param sourceComponents components of the source CRS.
- * @param targetCRS output coordinate reference system.
- * @param targetComponents components of the target CRS.
- * @return a coordinate operation from {@code sourceCRS} to {@code
targetCRS}.
- * @throws FactoryException if the operation can not be constructed.
- */
- protected CoordinateOperation createOperationStep(
- final CoordinateReferenceSystem sourceCRS, final List<? extends
SingleCRS> sourceComponents,
- final CoordinateReferenceSystem targetCRS, final List<? extends
SingleCRS> targetComponents)
- throws FactoryException
- {
- final SubOperationInfo[] infos = new
SubOperationInfo[targetComponents.size()];
- final boolean[] sourceIsUsed = new boolean[sourceComponents.size()];
- final CoordinateReferenceSystem[] stepComponents = new
CoordinateReferenceSystem[infos.length];
- /*
- * Operations found are stored in 'infos', but are not yet wrapped in
PassThroughOperations.
- * We need to know first if some ordinate values need reordering for
matching the target CRS
- * order. We also need to know if any source ordinates should be
dropped.
- */
- for (int i=0; i<infos.length; i++) {
- if ((infos[i] = SubOperationInfo.create(this, sourceIsUsed,
sourceComponents, targetComponents.get(i))) == null) {
- throw new
OperationNotFoundException(notFoundMessage(sourceCRS, targetCRS));
- }
- stepComponents[i] = infos[i].operation.getSourceCRS();
- }
- /*
- * At this point, a coordinate operation has been found for all
components of the target CRS.
- * However the CoordinateOperation.getSourceCRS() values are not
necessarily in the same order
- * than the components of the source CRS given to this method, and
some dimensions may be dropped.
- * The matrix computed by sourceToSelected(…) gives us the
rearrangement needed for the coordinate
- * operations that we just found.
- */
- int remainingSourceDimensions = 0;
- for (final SubOperationInfo component : infos) {
- remainingSourceDimensions += component.endAtDimension -
component.startAtDimension;
- }
- final Matrix select = SubOperationInfo.sourceToSelected(
- sourceCRS.getCoordinateSystem().getDimension(),
remainingSourceDimensions, infos);
- /*
- * First, we need a CRS matching the above-cited rearrangement. That
CRS will be named 'stepSourceCRS'
- * and its components will be named 'stepComponents'. Then we will
execute a loop in which each component
- * is progressively (one by one) updated from a source component to a
target component. A new step CRS is
- * recreated each time, since it will be needed for each
PassThroughOperation.
- */
- CoordinateReferenceSystem stepSourceCRS;
- CoordinateOperation operation;
- if (select.isIdentity()) {
- stepSourceCRS = sourceCRS; // No rearrangement - we
can use source CRS as-is.
- operation = null;
- } else {
- if (stepComponents.length == 1) {
- stepSourceCRS = stepComponents[0]; // Slight optimization
of the next block (in the 'else' case).
- } else {
- stepSourceCRS =
factorySIS.getCRSFactory().createCompoundCRS(derivedFrom(sourceCRS),
stepComponents);
- }
- operation = createFromAffineTransform(AXIS_CHANGES, sourceCRS,
stepSourceCRS, select);
- }
- /*
- * For each sub-operation, create a PassThroughOperation for the
(stepSourceCRS → stepTargetCRS) operation.
- * Each source CRS inside this loop will be for dimensions at indices
[startAtDimension … endAtDimension-1].
- * Note that those indices are not necessarily the same than the
indices in the fields of the same name in
- * SubOperationInfo, because those indices are not relative to the
same CompoundCRS.
- */
- int endAtDimension = 0;
- final int startOfIdentity = SubOperationInfo.startOfIdentity(infos);
- for (int i=0; i<stepComponents.length; i++) {
- final CoordinateReferenceSystem source = stepComponents[i];
- final CoordinateReferenceSystem target = targetComponents.get(i);
- CoordinateOperation subOperation = infos[i].operation;
- final MathTransform subTransform = subOperation.getMathTransform();
- /*
- * In order to compute 'stepTargetCRS', replace in-place a single
element in 'stepComponents'.
- * For each step except the last one, 'stepTargetCRS' is a mix of
target and source CRS. Only
- * after the loop finished, 'stepTargetCRS' will become the
complete targetCRS definition.
- */
- final CoordinateReferenceSystem stepTargetCRS;
- stepComponents[i] = target;
- if (i >= startOfIdentity) {
- stepTargetCRS = targetCRS; // If all remaining
transforms are identity, we reached the final CRS.
- } else if (subTransform.isIdentity()) {
- stepTargetCRS = stepSourceCRS; // In any identity
transform, the source and target CRS are equal.
- } else if (stepComponents.length == 1) {
- stepTargetCRS = target; // Slight optimization
of the next block.
- } else {
- stepTargetCRS =
ReferencingServices.getInstance().createCompoundCRS(
- factorySIS.getCRSFactory(), factorySIS.getCSFactory(),
derivedFrom(target), stepComponents);
- }
- int delta = source.getCoordinateSystem().getDimension();
- final int startAtDimension = endAtDimension;
- endAtDimension += delta;
- /*
- * Constructs the pass through transform only if there is at least
one ordinate to pass.
- * Actually the code below would work inconditionally, but we
perform this check anyway
- * for avoiding the creation of intermediate objects.
- */
- if (!(startAtDimension == 0 && endAtDimension ==
remainingSourceDimensions)) {
- final Map<String,?> properties =
IdentifiedObjects.getProperties(subOperation);
- /*
- * The DefaultPassThroughOperation constuctor expect a
SingleOperation.
- * In most case, the 'subOperation' is already of this kind.
However if
- * it is not, try to copy it in such object.
- */
- final SingleOperation op;
- if (subOperation instanceof SingleOperation) {
- op = (SingleOperation) subOperation;
- } else {
- op = factorySIS.createSingleOperation(properties,
- subOperation.getSourceCRS(),
subOperation.getTargetCRS(), null,
- new DefaultOperationMethod(subTransform),
subTransform);
- }
- subOperation = new DefaultPassThroughOperation(properties,
stepSourceCRS, stepTargetCRS,
- op, startAtDimension, remainingSourceDimensions -
endAtDimension);
- }
- /*
- * Concatenate the operation with the ones we have found so far,
and use the current 'stepTargetCRS'
- * as the source CRS for the next operation step. We also need to
adjust the dimension indices,
- * since the previous operations may have removed some dimensions.
Note that the delta may also
- * be negative in a few occasions.
- */
- operation = concatenate(operation, subOperation);
- stepSourceCRS = stepTargetCRS;
- delta -= target.getCoordinateSystem().getDimension();
- endAtDimension -= delta;
- remainingSourceDimensions -= delta;
- }
- return operation;
- }
-
-
-
-
-
-
/////////////////////////////////////////////////////////////////////////////////
-
/////////////////////////////////////////////////////////////////////////////////
- ////////////
////////////
- //////////// M I S C E L L A N E O U S
////////////
- ////////////
////////////
-
/////////////////////////////////////////////////////////////////////////////////
-
/////////////////////////////////////////////////////////////////////////////////
-
- /**
- * Creates a coordinate operation from a matrix, which usually describes
an affine transform.
- * A default {@link OperationMethod} object is given to this transform. In
the special case
- * where the {@code name} identifier is {@link #DATUM_SHIFT} or {@link
#ELLIPSOID_CHANGE},
- * the operation will be a {@link Transformation} instance instead of
{@link Conversion}.
- *
- * @param name The identifier for the operation to be created.
- * @param sourceCRS The source coordinate reference system.
- * @param targetCRS The target coordinate reference system.
- * @param matrix The matrix which describe an affine transform
operation.
- * @return The conversion or transformation.
- * @throws FactoryException if the operation can not be created.
- */
- private CoordinateOperation createFromAffineTransform(final Identifier
name,
- final
CoordinateReferenceSystem sourceCRS,
- final
CoordinateReferenceSystem targetCRS,
- final Matrix
matrix)
- throws FactoryException
- {
- final MathTransform transform =
factorySIS.getMathTransformFactory().createAffineTransform(matrix);
- return createFromMathTransform(properties(name), sourceCRS, targetCRS,
transform, null, null, null);
}
/**
@@ -943,13 +194,13 @@ public class CoordinateOperationInferenc
* @return A coordinate operation using the specified math transform.
* @throws FactoryException if the operation can not be created.
*/
- private CoordinateOperation createFromMathTransform(final
Map<String,Object> properties,
- final
CoordinateReferenceSystem sourceCRS,
- final
CoordinateReferenceSystem targetCRS,
- final MathTransform
transform,
- OperationMethod
method,
- final
ParameterValueGroup parameters,
- Class<? extends
CoordinateOperation> type)
+ final CoordinateOperation createFromMathTransform(final Map<String,Object>
properties,
+ final
CoordinateReferenceSystem sourceCRS,
+ final
CoordinateReferenceSystem targetCRS,
+ final MathTransform
transform,
+ OperationMethod
method,
+ final
ParameterValueGroup parameters,
+ Class<? extends
CoordinateOperation> type)
throws FactoryException
{
/*
@@ -1013,108 +264,15 @@ public class CoordinateOperationInferenc
/**
* Creates the inverse of the given operation.
*/
- private CoordinateOperation inverse(final SingleOperation op) throws
NoninvertibleTransformException, FactoryException {
+ final CoordinateOperation inverse(final SingleOperation op) throws
NoninvertibleTransformException, FactoryException {
final CoordinateReferenceSystem sourceCRS = op.getSourceCRS();
final CoordinateReferenceSystem targetCRS = op.getTargetCRS();
final MathTransform transform = op.getMathTransform().inverse();
+ Class<? extends CoordinateOperation> type = null;
+ if (op instanceof Transformation) type = Transformation.class;
+ else if (op instanceof Conversion) type = Conversion.class;
return createFromMathTransform(properties(INVERSE_OPERATION),
targetCRS, sourceCRS,
- transform, InverseOperationMethod.create(op.getMethod()),
null, null);
- }
-
- /**
- * Concatenates two operation steps.
- * The new concatenated operation gets an automatically generated name.
- *
- * <div class="section">Special case</div>
- * If one of the given operation steps performs a change of axis order or
units,
- * then that change will be merged with the other operation instead of
creating an {@link ConcatenatedOperation}.
- *
- * @param step1 The first step, or {@code null} for the identity
operation.
- * @param step2 The second step, or {@code null} for the identity
operation.
- * @return A concatenated operation, or {@code null} if all arguments were
null.
- * @throws FactoryException if the operation can't be constructed.
- */
- private CoordinateOperation concatenate(final CoordinateOperation step1,
- final CoordinateOperation step2)
- throws FactoryException
- {
- if (isIdentity(step1)) return step2;
- if (isIdentity(step2)) return step1;
- final MathTransform mt1 = step1.getMathTransform();
- final MathTransform mt2 = step2.getMathTransform();
- final CoordinateReferenceSystem sourceCRS = step1.getSourceCRS();
- final CoordinateReferenceSystem targetCRS = step2.getTargetCRS();
- /*
- * If one of the transform performs nothing more than a change of axis
order or units, do
- * not expose that conversion in a ConcatenatedTransform. Instead,
merge that conversion
- * with the "main" operation. The intend is to simplify the operation
chain by hidding
- * trivial operations.
- */
- CoordinateOperation main = null;
- if (step1.getName() == AXIS_CHANGES && mt1.getSourceDimensions() ==
mt1.getTargetDimensions()) main = step2;
- if (step2.getName() == AXIS_CHANGES && mt2.getSourceDimensions() ==
mt2.getTargetDimensions()) main = step1;
- if (main instanceof SingleOperation) {
- final SingleOperation op = (SingleOperation) main;
- final MathTransform mt =
factorySIS.getMathTransformFactory().createConcatenatedTransform(mt1, mt2);
- main = createFromMathTransform(new
HashMap<>(IdentifiedObjects.getProperties(main)),
- sourceCRS, targetCRS, mt, op.getMethod(),
op.getParameterValues(),
- (main instanceof Transformation) ? Transformation.class :
- (main instanceof Conversion) ? Conversion.class :
SingleOperation.class);
- } else {
- main = factory.createConcatenatedOperation(defaultName(sourceCRS,
targetCRS), step1, step2);
- }
- /*
- * Sometime we get a concatenated operation made of an operation
followed by its inverse.
- * We can identity those case when the associated MathTransform is the
identity transform.
- * In such case, simplify by replacing the ConcatenatedTransform by a
SingleTransform.
- */
- if (main instanceof ConcatenatedOperation &&
main.getMathTransform().isIdentity()) {
- Class<? extends CoordinateOperation> type = null;
- for (final CoordinateOperation component :
((ConcatenatedOperation) main).getOperations()) {
- if (component instanceof Transformation) {
- type = Transformation.class;
- break;
- }
- }
- main = createFromMathTransform(new
HashMap<>(IdentifiedObjects.getProperties(main)),
- main.getSourceCRS(), main.getTargetCRS(),
main.getMathTransform(), null, null, type);
- }
- return main;
- }
-
- /**
- * Concatenates three transformation steps. If the first and/or the last
operation is an {@link #AXIS_CHANGES},
- * then it will be included as part of the second operation instead of
creating a {@link ConcatenatedOperation}.
- * If a concatenated operation is created, it will get an automatically
generated name.
- *
- * @param step1 The first step, or {@code null} for the identity
operation.
- * @param step2 The second step, or {@code null} for the identity
operation.
- * @param step3 The third step, or {@code null} for the identity
operation.
- * @return A concatenated operation, or {@code null} if all arguments were
null.
- * @throws FactoryException if the operation can not be constructed.
- */
- protected CoordinateOperation concatenate(final CoordinateOperation step1,
- final CoordinateOperation step2,
- final CoordinateOperation step3)
- throws FactoryException
- {
- if (isIdentity(step1)) return concatenate(step2, step3);
- if (isIdentity(step2)) return concatenate(step1, step3);
- if (isIdentity(step3)) return concatenate(step1, step2);
- if (step1.getName() == AXIS_CHANGES) return
concatenate(concatenate(step1, step2), step3);
- if (step3.getName() == AXIS_CHANGES) return concatenate(step1,
concatenate(step2, step3));
- final Map<String,?> properties = defaultName(step1.getSourceCRS(),
step3.getTargetCRS());
- return factory.createConcatenatedOperation(properties, step1, step2,
step3);
- }
-
- /**
- * Returns {@code true} if the specified operation is an identity
conversion.
- * This method always returns {@code false} for transformations even if
their
- * associated math transform is an identity one, because such
transformations
- * are usually datum shift and must be visible.
- */
- private static boolean isIdentity(final CoordinateOperation operation) {
- return (operation == null) || ((operation instanceof Conversion) &&
operation.getMathTransform().isIdentity());
+ transform, InverseOperationMethod.create(op.getMethod()),
null, type);
}
/**
@@ -1131,7 +289,7 @@ public class CoordinateOperationInferenc
* @param name The name to put in a map.
* @return a modifiable map containing the given name. Callers can put
other entries in this map.
*/
- private static Map<String,Object> properties(final Identifier name) {
+ static Map<String,Object> properties(final Identifier name) {
final Map<String,Object> properties = new HashMap<>(4);
properties.put(CoordinateOperation.NAME_KEY, name);
if ((name == DATUM_SHIFT) || (name == ELLIPSOID_CHANGE)) {
@@ -1141,55 +299,4 @@ public class CoordinateOperationInferenc
}
return properties;
}
-
- /**
- * Returns the given name in a singleton map.
- */
- private static Map<String,?> properties(final String name) {
- return Collections.singletonMap(IdentifiedObject.NAME_KEY, name);
- }
-
- /**
- * Returns a name for an object derived from the specified one.
- * This method builds a name of the form "{@literal <original identifier>}
(step 1)"
- * where "(step 1)" may be replaced by "(step 2)", "(step 3)", <i>etc.</i>
if this
- * method has already been invoked for the same identifier (directly or
indirectly).
- */
- private Map<String,?> derivedFrom(final IdentifiedObject object) {
- Identifier oldID = object.getName();
- Object p = identifierOfStepCRS.get(oldID);
- if (p instanceof Identifier) {
- oldID = (Identifier) p;
- p = identifierOfStepCRS.get(oldID);
- }
- final int count = (p != null) ? (Integer) p + 1 : 1;
- final Identifier newID = new NamedIdentifier(Citations.SIS,
oldID.getCode() + " (step " + count + ')');
- identifierOfStepCRS.put(newID, oldID);
- identifierOfStepCRS.put(oldID, count);
-
- final Map<String,Object> properties = new HashMap<>(4);
- properties.put(IdentifiedObject.NAME_KEY, newID);
- properties.put(IdentifiedObject.REMARKS_KEY,
Vocabulary.formatInternational(
- Vocabulary.Keys.DerivedFrom_1,
CRSPair.label(object)));
- return properties;
- }
-
- /**
- * Returns a name for a transformation between two CRS.
- */
- private static Map<String,?> defaultName(CoordinateReferenceSystem source,
CoordinateReferenceSystem target) {
- return properties(CRSPair.label(source) + " → " +
CRSPair.label(target));
- }
-
- /**
- * Returns an error message for "No path found from sourceCRS to
targetCRS".
- * This is used for the construction of {@link OperationNotFoundException}.
- *
- * @param source the source CRS.
- * @param target the target CRS.
- * @return A default error message.
- */
- private static String notFoundMessage(final IdentifiedObject source, final
IdentifiedObject target) {
- return Errors.format(Errors.Keys.CoordinateOperationNotFound_2,
CRSPair.label(source), CRSPair.label(target));
- }
}
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java?rev=1738311&r1=1738310&r2=1738311&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
[UTF-8] Fri Apr 8 21:48:58 2016
@@ -30,23 +30,16 @@ import org.opengis.referencing.crs.*;
import org.opengis.referencing.datum.*;
import org.opengis.referencing.operation.*;
import org.opengis.metadata.Identifier;
-import org.opengis.metadata.extent.Extent;
-import org.opengis.metadata.extent.GeographicBoundingBox;
-import org.opengis.metadata.quality.PositionalAccuracy;
import org.opengis.parameter.ParameterValueGroup;
-import org.opengis.parameter.ParameterDescriptorGroup;
import org.apache.sis.internal.metadata.AxisDirections;
import org.apache.sis.internal.metadata.VerticalDatumTypes;
import org.apache.sis.internal.metadata.ReferencingServices;
import org.apache.sis.internal.referencing.ReferencingUtilities;
-import org.apache.sis.internal.referencing.CoordinateOperations;
-import org.apache.sis.internal.referencing.PositionalAccuracyConstant;
import org.apache.sis.internal.referencing.provider.Geographic2Dto3D;
import org.apache.sis.internal.referencing.provider.Geographic3Dto2D;
import org.apache.sis.internal.referencing.provider.GeographicToGeocentric;
import org.apache.sis.internal.referencing.provider.GeocentricToGeographic;
import org.apache.sis.internal.referencing.provider.GeocentricAffine;
-import org.apache.sis.internal.referencing.provider.Affine;
import org.apache.sis.internal.util.Constants;
import org.apache.sis.measure.Units;
import org.apache.sis.metadata.iso.citation.Citations;
@@ -60,7 +53,6 @@ import org.apache.sis.referencing.cs.Coo
import org.apache.sis.referencing.datum.BursaWolfParameters;
import org.apache.sis.referencing.datum.DefaultGeodeticDatum;
import org.apache.sis.referencing.operation.matrix.Matrices;
-import org.apache.sis.referencing.operation.transform.MathTransforms;
import
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.resources.Errors;
@@ -68,9 +60,6 @@ import org.apache.sis.util.resources.Voc
import static org.apache.sis.util.Utilities.equalsIgnoreMetadata;
-// Branch-dependent imports
-import java.util.Objects;
-
/**
* Infers a conversion of transformation path from a source CRS to a target
CRS.
@@ -92,7 +81,7 @@ import java.util.Objects;
*
* @see
DefaultCoordinateOperationFactory#createOperation(CoordinateReferenceSystem,
CoordinateReferenceSystem, CoordinateOperationContext)
*/
-public class CoordinateOperationInference {
+public class CoordinateOperationInference extends CoordinateOperationFinder {
/**
* The accuracy threshold (in metres) for allowing the use of Molodensky
approximation instead than the
* Geocentric Translation method. The accuracy of datum shifts with
Molodensky approximation is about 5
@@ -105,76 +94,6 @@ public class CoordinateOperationInferenc
private static final double MOLODENSKY_ACCURACY = 5;
/**
- * The identifier for an identity operation.
- */
- private static final Identifier IDENTITY =
createIdentifier(Vocabulary.Keys.Identity);
-
- /**
- * The identifier for conversion using an affine transform for axis
swapping and/or unit conversions.
- */
- private static final Identifier AXIS_CHANGES =
createIdentifier(Vocabulary.Keys.AxisChanges);
-
- /**
- * The identifier for a transformation which is a datum shift without
{@link BursaWolfParameters}.
- * Only the changes in ellipsoid axis-length are taken in account.
- * Such ellipsoid shifts are approximative and may have 1 kilometre error.
- *
- * @see
org.apache.sis.internal.referencing.PositionalAccuracyConstan#DATUM_SHIFT_OMITTED
- */
- private static final Identifier ELLIPSOID_CHANGE =
createIdentifier(Vocabulary.Keys.EllipsoidChange);
-
- /**
- * The identifier for a transformation which is a datum shift.
- *
- * @see
org.apache.sis.internal.referencing.PositionalAccuracyConstant#DATUM_SHIFT_APPLIED
- */
- private static final Identifier DATUM_SHIFT =
createIdentifier(Vocabulary.Keys.DatumShift);
-
- /**
- * The identifier for a geocentric conversion.
- */
- private static final Identifier GEOCENTRIC_CONVERSION =
createIdentifier(Vocabulary.Keys.GeocentricConversion);
-
- /**
- * The identifier for an inverse operation.
- */
- private static final Identifier INVERSE_OPERATION =
createIdentifier(Vocabulary.Keys.InverseOperation);
-
- /**
- * Creates an identifier in the Apache SIS namespace for the given
vocabulary key.
- */
- private static Identifier createIdentifier(final short key) {
- return new NamedIdentifier(Citations.SIS,
Vocabulary.formatInternational(key));
- }
-
- /**
- * The factory to use for creating coordinate operations.
- */
- private final CoordinateOperationFactory factory;
-
- /**
- * Used only when we need a SIS-specific method.
- */
- private final DefaultCoordinateOperationFactory factorySIS;
-
- /**
- * The spatio-temporal area of interest, or {@code null} if none.
- */
- private Extent areaOfInterest;
-
- /**
- * The geographic component of the area of interest, or {@code null} if
none.
- */
- private GeographicBoundingBox bbox;
-
- /**
- * The desired accuracy in metres, or 0 for the best accuracy available.
- *
- * @see #MOLODENSKY_ACCURACY
- */
- private double desiredAccuracy;
-
- /**
* Identifiers used as the basis for identifier of CRS used as an
intermediate step.
* The values can be of two kinds:
*
@@ -204,15 +123,7 @@ public class CoordinateOperationInferenc
public CoordinateOperationInference(final CoordinateOperationFactory
factory,
final CoordinateOperationContext
context)
{
- ArgumentChecks.ensureNonNull("factory", factory);
- this.factory = factory;
- factorySIS = (factory instanceof DefaultCoordinateOperationFactory)
- ? (DefaultCoordinateOperationFactory) factory :
CoordinateOperations.factory();
- if (context != null) {
- areaOfInterest = context.getAreaOfInterest();
- desiredAccuracy = context.getDesiredAccuracy();
- bbox = context.getGeographicBoundingBox();
- }
+ super(factory, context);
identifierOfStepCRS = new HashMap<>(8);
previousSearches = new HashMap<>(8);
}
@@ -908,120 +819,6 @@ public class CoordinateOperationInferenc
}
/**
- * Creates a coordinate operation from a math transform.
- * The method performs the following steps:
- *
- * <ul class="verbose">
- * <li>If the given {@code transform} is already an instance of {@code
CoordinateOperation} and if its properties
- * (operation method, source and target CRS) are compatible with the
arguments values, then that operation is
- * returned as-is.
- *
- * <div class="note"><b>Note:</b> we do not have many objects that
are both a {@code CoordinateOperation}
- * and a {@code MathTransform}, but that combination is not
forbidden. Since such practice is sometime
- * convenient for the implementor, Apache SIS allows that.</div></li>
- *
- * <li>If the given {@code type} is null, then this method infers the
type from whether the given properties
- * specify and accuracy or not. If those properties were created by
the {@link #properties(Identifier)}
- * method, then the operation will be a {@link Transformation}
instance instead of {@link Conversion} if
- * the {@code name} identifier was {@link #DATUM_SHIFT} or {@link
#ELLIPSOID_CHANGE}.</li>
- *
- * <li>If the given {@code method} is {@code null}, then infer an
operation method by inspecting the given transform.
- * The transform needs to implement the {@link
org.apache.sis.parameter.Parameterized} interface in order to allow
- * operation method discovery.</li>
- *
- * <li>Delegate to {@link
DefaultCoordinateOperationFactory#createSingleOperation
- * DefaultCoordinateOperationFactory.createSingleOperation(…)}.</li>
- * </ul>
- *
- * @param properties The properties to give to the operation, as a
modifiable map.
- * @param sourceCRS The source coordinate reference system.
- * @param targetCRS The destination coordinate reference system.
- * @param transform The math transform.
- * @param method The operation method, or {@code null} if unknown.
- * @param parameters The operations parameters, or {@code null} for
automatic detection (not always reliable).
- * @param type {@code Conversion.class}, {@code
Transformation.class}, or {@code null} if unknown.
- * @return A coordinate operation using the specified math transform.
- * @throws FactoryException if the operation can not be created.
- */
- private CoordinateOperation createFromMathTransform(final
Map<String,Object> properties,
- final
CoordinateReferenceSystem sourceCRS,
- final
CoordinateReferenceSystem targetCRS,
- final MathTransform
transform,
- OperationMethod
method,
- final
ParameterValueGroup parameters,
- Class<? extends
CoordinateOperation> type)
- throws FactoryException
- {
- /*
- * If the specified math transform is already a coordinate operation,
and if its properties (method,
- * source and target CRS) are compatible with the specified ones, then
that operation is returned as-is.
- */
- if (transform instanceof CoordinateOperation) {
- final CoordinateOperation operation = (CoordinateOperation)
transform;
- if (Objects.equals(operation.getSourceCRS(), sourceCRS) &&
- Objects.equals(operation.getTargetCRS(), targetCRS) &&
- Objects.equals(operation.getMathTransform(), transform) &&
- (method == null || !(operation instanceof SingleOperation) ||
- Objects.equals(((SingleOperation) operation).getMethod(),
method)))
- {
- return operation;
- }
- }
- /*
- * If the operation type was not explicitely specified, infers it from
whether an accuracy is specified
- * or not. In principle, only transformations has an accuracy
property; conversions do not. This policy
- * is applied by the properties(Identifier) method in this class.
- */
- if (type == null) {
- type =
properties.containsKey(CoordinateOperation.COORDINATE_OPERATION_ACCURACY_KEY)
- ? Transformation.class : Conversion.class;
- }
- /*
- * The operation method is mandatory. If the user did not provided
one, we need to infer it ourselves.
- * If we fail to infer an OperationMethod, let it to null - the
exception will be thrown by the factory.
- */
- if (method == null) {
- final Matrix matrix = MathTransforms.getMatrix(transform);
- if (matrix != null) {
- method = Affine.getProvider(transform.getSourceDimensions(),
transform.getTargetDimensions(), Matrices.isAffine(matrix));
- } else {
- final ParameterDescriptorGroup descriptor =
AbstractCoordinateOperation.getParameterDescriptors(transform);
- if (descriptor != null) {
- final Identifier name = descriptor.getName();
- if (name != null) {
- method = factory.getOperationMethod(name.getCode());
- }
- if (method == null) {
- method = factory.createOperationMethod(properties,
- sourceCRS.getCoordinateSystem().getDimension(),
- targetCRS.getCoordinateSystem().getDimension(),
- descriptor);
- }
- }
- }
- }
- if (parameters != null) {
- properties.put(ReferencingServices.PARAMETERS_KEY, parameters);
- }
- properties.put(ReferencingServices.OPERATION_TYPE_KEY, type);
- if (Conversion.class.isAssignableFrom(type) && transform.isIdentity())
{
- properties.replace(IdentifiedObject.NAME_KEY, AXIS_CHANGES,
IDENTITY);
- }
- return factorySIS.createSingleOperation(properties, sourceCRS,
targetCRS, null, method, transform);
- }
-
- /**
- * Creates the inverse of the given operation.
- */
- private CoordinateOperation inverse(final SingleOperation op) throws
NoninvertibleTransformException, FactoryException {
- final CoordinateReferenceSystem sourceCRS = op.getSourceCRS();
- final CoordinateReferenceSystem targetCRS = op.getTargetCRS();
- final MathTransform transform = op.getMathTransform().inverse();
- return createFromMathTransform(properties(INVERSE_OPERATION),
targetCRS, sourceCRS,
- transform, InverseOperationMethod.create(op.getMethod()),
null, null);
- }
-
- /**
* Concatenates two operation steps.
* The new concatenated operation gets an automatically generated name.
*
@@ -1118,31 +915,6 @@ public class CoordinateOperationInferenc
}
/**
- * Returns the specified identifier in a map to be given to coordinate
operation constructors.
- * In the special case where the {@code name} identifier is {@link
#DATUM_SHIFT} or {@link #ELLIPSOID_CHANGE},
- * the map will contains extra informations like positional accuracy.
- *
- * <div class="note"><b>Note:</b>
- * in the datum shift case, an operation version is mandatory but unknown
at this time.
- * However, we noticed that the EPSG database do not always defines a
version neither.
- * Consequently, the Apache SIS implementation relaxes the rule requiring
an operation
- * version and we do not try to provide this information here for
now.</div>
- *
- * @param name The name to put in a map.
- * @return a modifiable map containing the given name. Callers can put
other entries in this map.
- */
- private static Map<String,Object> properties(final Identifier name) {
- final Map<String,Object> properties = new HashMap<>(4);
- properties.put(CoordinateOperation.NAME_KEY, name);
- if ((name == DATUM_SHIFT) || (name == ELLIPSOID_CHANGE)) {
-
properties.put(CoordinateOperation.COORDINATE_OPERATION_ACCURACY_KEY, new
PositionalAccuracy[] {
- (name == DATUM_SHIFT) ?
PositionalAccuracyConstant.DATUM_SHIFT_APPLIED
- :
PositionalAccuracyConstant.DATUM_SHIFT_OMITTED});
- }
- return properties;
- }
-
- /**
* Returns the given name in a singleton map.
*/
private static Map<String,?> properties(final String name) {