This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 5b922975574652694c63508b01d52c6d47735183
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Mon Apr 27 14:57:00 2026 +0200

    Reintroduce `DefiningConversion` with an optional property for specifying 
whether
    the conversion is fully-defined or apply between normalized coordinate 
systems.
---
 .../apache/sis/coverage/grid/GridCRSBuilder.java   |   6 +-
 .../geometry/wrapper/SpatialOperationContext.java  |   5 +-
 .../main/org/apache/sis/referencing/CRS.java       |   8 +-
 .../referencing/EllipsoidalHeightSeparator.java    |   8 +-
 .../sis/referencing/StandardDefinitions.java       |   4 +-
 .../sis/referencing/crs/DefaultDerivedCRS.java     |   3 +
 .../referencing/factory/GeodeticObjectFactory.java |  37 +++++
 .../referencing/operation/DefaultConversion.java   | 121 ++++++++-------
 .../DefaultCoordinateOperationFactory.java         |   2 +-
 .../referencing/operation/DefiningConversion.java  | 163 +++++++++++++++++++++
 .../factory/GeodeticObjectFactoryTest.java         |   5 +-
 .../operation/DefaultConversionTest.java           |   2 +-
 .../operation/HardCodedConversions.java            |   2 +-
 .../transform/DefaultMathTransformFactoryTest.java |   4 +-
 14 files changed, 289 insertions(+), 81 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
index 48d5c14d38..cb36deb54c 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
@@ -49,7 +49,7 @@ import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.NamedIdentifier;
 import org.apache.sis.referencing.IdentifiedObjects;
 import org.apache.sis.referencing.cs.AbstractCS;
-import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.DefiningConversion;
 import org.apache.sis.referencing.operation.DefaultOperationMethod;
 import org.apache.sis.referencing.operation.transform.TransformSeparator;
 import org.apache.sis.referencing.factory.GeodeticObjectFactory;
@@ -166,7 +166,7 @@ final class GridCRSBuilder extends 
ReferencingFactoryContainer {
         this.locale     = locale;
         this.finalName  = name;
         this.properties = new HashMap<>(8);
-        properties.put(DefaultConversion.LOCALE_KEY, locale);
+        properties.put(DefiningConversion.LOCALE_KEY, locale);
         properties.put(ObjectDomain.SCOPE_KEY, SCOPE);
         grid.getGeographicExtent().ifPresent((domain) -> {
             properties.put(ObjectDomain.DOMAIN_OF_VALIDITY_KEY, new 
DefaultExtent(null, domain, null, null));
@@ -276,7 +276,7 @@ final class GridCRSBuilder extends 
ReferencingFactoryContainer {
         final ParameterValueGroup params = 
METHOD.getParameters().createValue();
         params.parameter(NAME_PARAM).setValue(finalName);
         params.parameter(ANCHOR_PARAM).setValue(anchor);
-        final var conversion = new 
DefaultConversion(properties(METHOD.getName()), METHOD, gridToCRS.inverse(), 
params);
+        final var conversion = new 
DefiningConversion(properties(METHOD.getName()), METHOD, gridToCRS.inverse(), 
params);
         return getCRSFactory().createDerivedCRS(properties(name), baseCRS, 
conversion, cs);
     }
 
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java
index 709d962403..46caa43a23 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java
@@ -37,12 +37,13 @@ import org.apache.sis.referencing.ImmutableIdentifier;
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.internal.shared.ReferencingFactoryContainer;
 import org.apache.sis.referencing.internal.shared.ReferencingUtilities;
-import org.apache.sis.util.collection.BackingStoreException;
 import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.DefiningConversion;
 import org.apache.sis.referencing.crs.DefaultProjectedCRS;
 import org.apache.sis.measure.Units;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.internal.shared.Constants;
+import org.apache.sis.util.collection.BackingStoreException;
 import org.apache.sis.metadata.iso.citation.Citations;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
@@ -419,7 +420,7 @@ select: if (commonCRS == null) {
             if (CONTINUOUS_WRAPAROUND) {
                 p.parameter(Constants.CENTRAL_MERIDIAN).setValue(longitude);
             }
-            final var conversion = new DefaultConversion(name, method, null, 
p);
+            final var conversion = new DefiningConversion(name, method, null, 
p);
             return new DefaultProjectedCRS(name, baseCRS, conversion, cartCS);
         }
 
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java
index c4f5ac8f37..fa2e0ab237 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java
@@ -84,7 +84,7 @@ import org.apache.sis.referencing.datum.DefaultPrimeMeridian;
 import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
 import org.apache.sis.referencing.operation.CoordinateOperationContext;
 import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory;
-import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.DefiningConversion;
 import org.apache.sis.referencing.factory.GeodeticObjectFactory;
 import org.apache.sis.referencing.factory.UnavailableFactoryException;
 import org.apache.sis.coordinate.DefaultCoordinateMetadata;
@@ -1769,8 +1769,10 @@ public final class CRS {
                     final var proj = (ProjectedCRS) crs;
                     final var base = (GeodeticCRS) 
getHorizontalComponent(proj.getBaseCRS());
                     Conversion fromBase = proj.getConversionFromBase();
-                    fromBase = new 
DefaultConversion(IdentifiedObjects.getProperties(fromBase),
-                            fromBase.getMethod(), null, 
fromBase.getParameterValues());
+                    fromBase = new DefiningConversion(
+                            IdentifiedObjects.getProperties(fromBase),
+                            fromBase.getMethod(), null,
+                            fromBase.getParameterValues());
                     return new DefaultProjectedCRS(properties, base, fromBase, 
(CartesianCS) cs);
                 }
                 /*
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
index f5c52cb9f6..9e30dca2f4 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/EllipsoidalHeightSeparator.java
@@ -32,7 +32,7 @@ import org.opengis.referencing.operation.Conversion;
 import org.apache.sis.referencing.internal.shared.AxisDirections;
 import org.apache.sis.referencing.cs.CoordinateSystems;
 import org.apache.sis.referencing.cs.AxisFilter;
-import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.DefiningConversion;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.referencing.factory.GeodeticObjectFactory;
@@ -152,8 +152,10 @@ final class EllipsoidalHeightSeparator implements 
AxisFilter {
              * constructor expects a normalized transform, while the 
`projection.getMathTransform()` may not
              * be normalized. So we are better to let constructor recreate the 
transform from the parameters.
              */
-            projection = new 
DefaultConversion(getPropertiesForModifiedCRS(projection),
-                                projection.getMethod(), null, 
projection.getParameterValues());
+            projection = new DefiningConversion(
+                    getPropertiesForModifiedCRS(projection),
+                    projection.getMethod(), null,
+                    projection.getParameterValues());
             return 
factory().createProjectedCRS(getPropertiesForModifiedCRS(crs), baseCRS, 
projection, (CartesianCS) cs);
         }
         throw new 
IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedType_1, 
crs.getClass()));
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/StandardDefinitions.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/StandardDefinitions.java
index 2c0621d8eb..e9de4d581f 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/StandardDefinitions.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/StandardDefinitions.java
@@ -48,7 +48,7 @@ import org.apache.sis.util.SimpleInternationalString;
 import org.apache.sis.util.internal.shared.Constants;
 import org.apache.sis.metadata.internal.shared.AxisNames;
 import org.apache.sis.referencing.internal.Resources;
-import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.DefiningConversion;
 import org.apache.sis.referencing.operation.provider.Mercator1SP;
 import org.apache.sis.referencing.operation.provider.PseudoMercator;
 import org.apache.sis.referencing.operation.provider.TransverseMercator;
@@ -228,7 +228,7 @@ final class StandardDefinitions {
         }
         final ParameterValueGroup parameters = 
method.getParameters().createValue();
         String name = setup.apply(parameters);
-        final var conversion = new DefaultConversion(properties(0, name, null, 
false), method, null, parameters);
+        final var conversion = new DefiningConversion(properties(0, name, 
null, false), method, null, parameters);
         name = baseCRS.getName().getCode() + " / " + name;
         return new DefaultProjectedCRS(properties(code, name, null, false), 
baseCRS, conversion, derivedCS);
     }
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
index 579812d24e..4164635b3c 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
@@ -513,6 +513,8 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS 
implements DerivedCRS
 
     /**
      * @hidden because nothing new to said.
+     *
+     * @return the hash code value.
      */
     @Override
     protected long computeHashCode() {
@@ -522,6 +524,7 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS 
implements DerivedCRS
     /**
      * Formats the inner part of the <i>Well Known Text</i> (WKT) 
representation of this CRS.
      *
+     * @param  formatter  the formatter where to format the inner content of 
this WKT element.
      * @return {@code "Fitted_CS"} (WKT 1) or a type-dependent keyword (WKT 2).
      */
     @Override
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
index 7675299eab..d86a516c4b 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
@@ -343,6 +343,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  ensemble    collection of reference frames which for low 
accuracy requirements may be considered to be
      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
      * @param  cs          the three-dimensional Cartesian coordinate system 
for the created CRS.
+     * @return the coordinate reference system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultGeocentricCRS#DefaultGeocentricCRS(Map, GeodeticDatum, 
DatumEnsemble, CartesianCS)
@@ -382,6 +383,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  axis0       the first  axis (e.g. “Geocentric X”).
      * @param  axis1       the second axis (e.g. “Geocentric Y”).
      * @param  axis2       the third  axis (e.g. “Geocentric Z”).
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultCartesianCS#DefaultCartesianCS(Map, CoordinateSystemAxis, 
CoordinateSystemAxis, CoordinateSystemAxis)
@@ -431,6 +433,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  ensemble    collection of reference frames which for low 
accuracy requirements may be considered to be
      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
      * @param  cs          the spherical coordinate system for the created CRS.
+     * @return the coordinate reference system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultGeocentricCRS#DefaultGeocentricCRS(Map, GeodeticDatum, 
DatumEnsemble, SphericalCS)
@@ -470,6 +473,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  axis0       the first  axis (e.g. “Spherical latitude”).
      * @param  axis1       the second axis (e.g. “Spherical longitude”).
      * @param  axis2       the third  axis (e.g. “Geocentric radius”).
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultSphericalCS#DefaultSphericalCS(Map, CoordinateSystemAxis, 
CoordinateSystemAxis, CoordinateSystemAxis)
@@ -506,6 +510,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  properties  name and other properties to give to the new object.
      * @param  axis0       the first  axis (e.g. “Spherical latitude”).
      * @param  axis1       the second axis (e.g. “Spherical longitude”).
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultSphericalCS#DefaultSphericalCS(Map, CoordinateSystemAxis, 
CoordinateSystemAxis)
@@ -556,6 +561,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  ensemble    collection of reference frames which for low 
accuracy requirements may be considered to be
      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
      * @param  cs          the two- or three-dimensional ellipsoidal 
coordinate system for the created <abbr>CRS</abbr>.
+     * @return the coordinate reference system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultGeographicCRS#DefaultGeographicCRS(Map, GeodeticDatum, 
DatumEnsemble, EllipsoidalCS)
@@ -647,6 +653,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  properties     name and other properties to give to the new 
object.
      * @param  ellipsoid      the ellipsoid to use in new geodetic reference 
frame.
      * @param  primeMeridian  the prime meridian to use in new geodetic 
reference frame.
+     * @return the datum for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultGeodeticDatum#DefaultGeodeticDatum(Map, Ellipsoid, 
PrimeMeridian)
@@ -677,6 +684,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  ellipsoid      the ellipsoid to use in new geodetic reference 
frame.
      * @param  primeMeridian  the prime meridian to use in new geodetic 
reference frame.
      * @param  epoch          the epoch to which the definition of the dynamic 
reference frame is referenced.
+     * @return the datum for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultGeodeticDatum.Dynamic#Dynamic(Map, Ellipsoid, 
PrimeMeridian, Temporal)
@@ -709,6 +717,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  properties   name and other properties to give to the new 
object.
      * @param  longitude    the longitude of prime meridian in supplied 
angular units East of Greenwich.
      * @param  angularUnit  the angular units of longitude.
+     * @return the prime meridian for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultPrimeMeridian#DefaultPrimeMeridian(Map, double, Unit)
@@ -744,6 +753,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  properties  name and other properties to give to the new object.
      * @param  axis0       the first  axis (e.g. “Geodetic latitude”).
      * @param  axis1       the second axis (e.g. “Geodetic longitude”).
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultEllipsoidalCS#DefaultEllipsoidalCS(Map, 
CoordinateSystemAxis, CoordinateSystemAxis)
@@ -781,6 +791,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  axis0       the first  axis (e.g. “Geodetic latitude”).
      * @param  axis1       the second axis (e.g. “Geodetic longitude”).
      * @param  axis2       the third  axis (e.g. “Ellipsoidal height”).
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultEllipsoidalCS#DefaultEllipsoidalCS(Map, 
CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis)
@@ -810,6 +821,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  semiMajorAxis  the equatorial radius in supplied linear units.
      * @param  semiMinorAxis  the polar radius in supplied linear units.
      * @param  unit           the linear units of ellipsoid axes.
+     * @return the ellipsoid for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultEllipsoid#createEllipsoid(Map, double, double, Unit)
@@ -839,6 +851,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  semiMajorAxis      the equatorial radius in supplied linear 
units.
      * @param  inverseFlattening  the eccentricity of ellipsoid.
      * @param  unit               the linear units of major axis.
+     * @return the ellipsoid for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultEllipsoid#createFlattenedSphere(Map, double, double, Unit)
@@ -891,6 +904,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  baseCRS        the geographic coordinate reference system to 
base projection on.
      * @param  baseToDerived  the defining conversion from a {@linkplain 
AxesConvention#NORMALIZED normalized} base to a normalized derived CRS.
      * @param  derivedCS      the coordinate system for the projected CRS.
+     * @return the coordinate reference system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultProjectedCRS#DefaultProjectedCRS(Map, GeographicCRS, 
Conversion, CartesianCS)
@@ -931,6 +945,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  properties  name and other properties to give to the new object.
      * @param  axis0       the first  axis (e.g. “Easting”).
      * @param  axis1       the second axis (e.g. “Northing”).
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultCartesianCS#DefaultCartesianCS(Map, CoordinateSystemAxis, 
CoordinateSystemAxis)
@@ -976,6 +991,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
      * @param  baseCRS        the coordinate reference system to base 
projection on. Shall be an instance of {@link SingleCRS}.
      * @param  baseToDerived  the defining conversion from a {@linkplain 
AxesConvention#NORMALIZED normalized} base to a normalized derived CRS.
      * @param  derivedCS      the coordinate system for the derived CRS.
+     * @return the coordinate reference system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultDerivedCRS#create(Map, SingleCRS, Conversion, 
CoordinateSystem)
@@ -1024,6 +1040,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * @param  ensemble    collection of reference frames which for low 
accuracy requirements may be considered to be
      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
      * @param  cs          the vertical coordinate system for the created 
<abbr>CRS</abbr>.
+     * @return the coordinate reference system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultVerticalCRS#DefaultVerticalCRS(Map, VerticalDatum, 
DatumEnsemble, VerticalCS)
@@ -1074,6 +1091,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      *
      * @param  properties  name and other properties to give to the new object.
      * @param  method      the realization method of the vertical datum, or 
{@code null} if none.
+     * @return the datum for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultVerticalDatum#DefaultVerticalDatum(Map, RealizationMethod)
@@ -1104,6 +1122,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * @param  properties  name and other properties to give to the new object.
      * @param  method      the realization method of the vertical datum, or 
{@code null} if none.
      * @param  epoch       the epoch to which the definition of the dynamic 
reference frame is referenced.
+     * @return the datum for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultVerticalDatum.Dynamic#Dynamic(Map, RealizationMethod, 
Temporal)
@@ -1140,6 +1159,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      *
      * @param  properties  name and other properties to give to the new object.
      * @param  axis        the single axis (e.g. “height” or “depth”).
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultVerticalCS#DefaultVerticalCS(Map, CoordinateSystemAxis)
@@ -1179,6 +1199,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * @param  ensemble    collection of datum which for low accuracy 
requirements may be considered to be
      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
      * @param  cs          the temporal coordinate system for the created 
<abbr>CRS</abbr>.
+     * @return the coordinate reference system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultTemporalCRS#DefaultTemporalCRS(Map, TemporalDatum, 
DatumEnsemble, TimeCS)
@@ -1228,6 +1249,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      *
      * @param  properties  name and other properties to give to the new object.
      * @param  origin      the date and time origin of this temporal datum.
+     * @return the datum for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultTemporalDatum#DefaultTemporalDatum(Map, Date)
@@ -1261,6 +1283,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      *
      * @param  properties  name and other properties to give to the new object.
      * @param  axis        the single axis.
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultTimeCS#DefaultTimeCS(Map, CoordinateSystemAxis)
@@ -1302,6 +1325,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * @param  ensemble    collection of datum which for low accuracy 
requirements may be considered to be
      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
      * @param  cs          the parametric coordinate system for the created 
<abbr>CRS</abbr>.
+     * @return the coordinate reference system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultParametricCRS#DefaultParametricCRS(Map, ParametricDatum, 
DatumEnsemble, ParametricCS)
@@ -1351,6 +1375,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * The default implementation creates a {@link DefaultParametricDatum} 
instance.
      *
      * @param  properties  name and other properties to give to the new object.
+     * @return the datum for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultParametricDatum#DefaultParametricDatum(Map)
@@ -1383,6 +1408,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      *
      * @param  properties  name and other properties to give to the new object.
      * @param  axis        the single axis.
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultParametricCS#DefaultParametricCS(Map, CoordinateSystemAxis)
@@ -1417,6 +1443,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      *
      * @param  properties  name and other properties to give to the new object.
      * @param  components  the sequence of coordinate reference systems making 
the compound CRS.
+     * @return the coordinate reference system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultCompoundCRS#DefaultCompoundCRS(Map, 
CoordinateReferenceSystem...)
@@ -1452,6 +1479,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * @param  properties  name and other properties to give to the new object.
      * @param  axis0       the first  axis.
      * @param  axis1       the second axis.
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultAffineCS#DefaultAffineCS(Map, CoordinateSystemAxis, 
CoordinateSystemAxis)
@@ -1497,6 +1525,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * @param  ensemble    collection of datum which for low accuracy 
requirements may be considered to be
      *                     insignificantly different from each other, or 
{@code null} if there is no such ensemble.
      * @param  cs          the coordinate system for the created 
<abbr>CRS</abbr>.
+     * @return the coordinate reference system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultEngineeringCRS#DefaultEngineeringCRS(Map, EngineeringDatum, 
CoordinateSystem)
@@ -1546,6 +1575,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * The default implementation creates a {@link DefaultEngineeringDatum} 
instance.
      *
      * @param  properties  name and other properties to give to the new object.
+     * @return the datum for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultEngineeringDatum#DefaultEngineeringDatum(Map)
@@ -1580,6 +1610,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * @param  axis0       the first  axis.
      * @param  axis1       the second axis.
      * @param  axis2       the third  axis.
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultAffineCS#DefaultAffineCS(Map, CoordinateSystemAxis, 
CoordinateSystemAxis, CoordinateSystemAxis)
@@ -1616,6 +1647,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * @param  axis0       the first  axis.
      * @param  axis1       the second axis.
      * @param  axis2       the third  axis.
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultCylindricalCS#DefaultCylindricalCS(Map, 
CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis)
@@ -1652,6 +1684,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * @param  properties  name and other properties to give to the new object.
      * @param  axis0       the first  axis.
      * @param  axis1       the second axis.
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultPolarCS#DefaultPolarCS(Map, CoordinateSystemAxis, 
CoordinateSystemAxis)
@@ -1686,6 +1719,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      *
      * @param  properties  name and other properties to give to the new object.
      * @param  axis        the single axis.
+     * @return the coordinate system for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultLinearCS#DefaultLinearCS(Map, CoordinateSystemAxis)
@@ -1715,6 +1749,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * @param  abbreviation  the coordinate axis abbreviation.
      * @param  direction     the axis direction.
      * @param  unit          the coordinate axis unit.
+     * @return the axis for the given properties.
      * @throws FactoryException if the object creation failed.
      *
      * @see DefaultCoordinateSystemAxis#DefaultCoordinateSystemAxis(Map, 
String, AxisDirection, Unit)
@@ -1744,6 +1779,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * <p>The default implementation delegates to {@link 
XML#unmarshal(String)}</p>
      *
      * @param  xml  coordinate reference system encoded in XML format.
+     * @return the coordinate reference system for the given <abbr>XML</abbr>.
      * @throws FactoryException if the object creation failed.
      *
      * @see XML#unmarshal(String)
@@ -1829,6 +1865,7 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
      * the {@link org.apache.sis.io.wkt.WKTFormat} class instead of this 
method.
      *
      * @param  wkt  coordinate system encoded in Well-Known Text format 
(version 1 or 2).
+     * @return the coordinate reference system for the given <abbr>WKT</abbr>.
      * @throws FactoryException if the object creation failed.
      *
      * @see org.apache.sis.io.wkt
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConversion.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConversion.java
index f3501c4cd6..0c5e6c178c 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConversion.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConversion.java
@@ -49,29 +49,16 @@ import org.opengis.referencing.crs.DerivedCRS;
 
 
 /**
- * A parameterized mathematical operation that converts coordinates to another 
CRS without any change of
- * {@linkplain org.apache.sis.referencing.datum.AbstractDatum datum}.
+ * A parameterized mathematical operation that converts coordinates
+ * to another <abbr>CRS</abbr> without any change of datum.
  * The best-known example of a coordinate conversion is a map projection.
  * The parameters describing coordinate conversions are defined rather than 
empirically derived.
  *
- * <p>This coordinate operation contains an {@linkplain DefaultOperationMethod 
operation method}, usually
- * with associated {@linkplain 
org.apache.sis.parameter.DefaultParameterValueGroup parameter values}.
- * In the SIS implementation, the parameter values can be either inferred from 
the
- * {@linkplain 
org.apache.sis.referencing.operation.transform.AbstractMathTransform math 
transform}
- * or explicitly provided at construction time in a <dfn>defining 
conversion</dfn> (see below).</p>
- *
- * <h2>Defining conversions</h2>
- * {@code OperationMethod} instances are generally created for a pair of 
existing {@linkplain #getSourceCRS() source}
- * and {@linkplain #getTargetCRS() target CRS}. But {@code Conversion} 
instances without those information may exist
- * temporarily while creating a {@linkplain 
org.apache.sis.referencing.crs.DefaultDerivedCRS derived} or
- * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS projected 
CRS}.
- * Those <i>defining conversions</i> have no source and target CRS since those 
elements are provided by the
- * derived or projected CRS themselves. This class provides a {@linkplain 
#DefaultConversion(Map, OperationMethod,
- * MathTransform, ParameterValueGroup) constructor} for such defining 
conversions.
- *
- * <p>After the source and target CRS become known, we can invoke the {@link 
#specialize specialize(…)} method
- * for {@linkplain DefaultMathTransformFactory#builder creating a math 
transform from the parameters}
- * and assign the source and target CRS to it.</p>
+ * <p>This coordinate operation contains an {@linkplain OperationMethod 
operation method},
+ * usually with associated {@linkplain ParameterValueGroup parameter values}.
+ * In the <abbr>SIS</abbr> implementation, the parameter values can be either 
inferred from the
+ * {@linkplain MathTransform math transform} or explicitly provided at 
construction time in a
+ * {@linkplain DefiningConversion defining conversion}.</p>
  *
  * <h2>Immutability and thread safety</h2>
  * This class is immutable and thus thread-safe if the property 
<em>values</em> (not necessarily the map itself)
@@ -80,7 +67,7 @@ import org.opengis.referencing.crs.DerivedCRS;
  * by many objects and passed between threads without synchronization.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.5
+ * @version 1.7
  *
  * @see DefaultTransformation
  *
@@ -134,7 +121,7 @@ public class DefaultConversion extends 
AbstractSingleOperation implements Conver
      * with different source and target datum, it does not accept to use such 
instances for
      * {@linkplain org.apache.sis.referencing.crs.DefaultDerivedCRS derived 
CRS} construction.</p>
      *
-     * <h4>Example</h4>
+     * <h5>Example</h5>
      * Converting time instants from a {@linkplain 
org.apache.sis.referencing.crs.DefaultTemporalCRS temporal CRS}
      * using the <i>January 1st, 1950</i> epoch to another temporal CRS using 
the <i>January 1st, 1970</i> epoch is
      * a datum change, since the epoch is part of {@linkplain 
org.apache.sis.referencing.datum.DefaultTemporalDatum
@@ -165,38 +152,15 @@ public class DefaultConversion extends 
AbstractSingleOperation implements Conver
 
     /**
      * Creates a defining conversion from the given transform and/or 
parameters.
-     * This conversion has no source and target CRS since those elements are 
usually unknown
-     * at <i>defining conversion</i> construction time.
-     * The source and target CRS will become known later, at the
-     * {@linkplain org.apache.sis.referencing.crs.DefaultDerivedCRS Derived 
CRS} or
-     * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS 
Projected CRS}
-     * construction time.
-     *
-     * <p>The {@code properties} map given in argument follows the same rules 
as for the
-     * {@linkplain #DefaultConversion(Map, CoordinateReferenceSystem, 
CoordinateReferenceSystem,
-     * CoordinateReferenceSystem, OperationMethod, MathTransform) above 
constructor}.</p>
-     *
-     * <h4>Transform and parameters arguments</h4>
-     * At least one of the {@code transform} or {@code parameters} argument 
must be non-null.
-     * If the caller supplies a {@code transform} argument, then it shall be a 
transform expecting
-     * {@linkplain org.apache.sis.referencing.cs.AxesConvention#NORMALIZED 
normalized} input coordinates
-     * and producing normalized output coordinates. See {@link 
org.apache.sis.referencing.cs.AxesConvention}
-     * for more information about what Apache SIS means by "normalized".
-     *
-     * <p>If the caller cannot yet supply a {@code MathTransform}, then (s)he 
shall supply the parameter values needed
-     * for creating that transform, with the possible omission of {@code 
"semi_major"} and {@code "semi_minor"} values.
-     * The semi-major and semi-minor parameter values will be set 
automatically when the
-     * {@link #specialize specialize(…)} method will be invoked.</p>
-     *
-     * <p>If both the {@code transform} and {@code parameters} arguments are 
non-null, then the latter should describe
-     * the parameters used for creating the transform. Those parameters will 
be stored for information purpose and can
-     * be given back by the {@link #getParameterValues()} method.</p>
      *
      * @param properties  the properties to be given to the identified object.
      * @param method      the operation method.
      * @param transform   transform from positions in the source CRS to 
positions in the target CRS, or {@code null}.
      * @param parameters  the {@code transform} parameter values, or {@code 
null}.
+     *
+     * @deprecated Moved to the {@link DefiningConversion} subclass.
      */
+    @Deprecated(since = "1.7", forRemoval = true)
     @SuppressWarnings("this-escape")    // False positive.
     public DefaultConversion(final Map<String,?>       properties,
                              final OperationMethod     method,
@@ -214,16 +178,39 @@ public class DefaultConversion extends 
AbstractSingleOperation implements Conver
     }
 
     /**
-     * Constructs a new conversion with the same values as the specified one, 
together with the
-     * specified source and target CRS. While the source conversion can be an 
arbitrary one,
-     * it is typically a defining conversion.
+     * Creates a new coordinate operation initialized from the given 
properties.
+     * It is caller's responsibility to set the following fields:
+     *
+     * <ul>
+     *   <li>{@link #sourceCRS}</li>
+     *   <li>{@link #targetCRS}</li>
+     *   <li>{@link #transform}</li>
+     *   <li>{@link #parameters}</li>
+     * </ul>
+     */
+    DefaultConversion(final Map<String,?> properties, final OperationMethod 
method) {
+        super(properties, method);
+    }
+
+    /**
+     * Constructs a new conversion with the same values as the specified one,
+     * together with the specified source and target <abbr>CRS</abbr>.
+     * While the source conversion can be an arbitrary one, it is typically a 
defining conversion.
+     *
+     * <p>The {@code normalized} argument is {@code true} if the defining 
conversion provides a normalized transform.
+     * In such case, an adjustment for axis directions and units of 
measurement will be added for matching the given
+     * source and target <abbr>CRS</abbr>s. If {@code normalized} is {@code 
false}, then the defining conversion shall
+     * provide the complete transform and no adjustments is added. This 
argument is ignored if the defining conversion
+     * already provides source and target <abbr>CRS</abbr>s.</p>
      *
      * @param definition  the defining conversion.
-     * @param source      the new source CRS.
-     * @param target      the new target CRS.
+     * @param normalized  whether the transform provided by the defining 
conversion is normalized.
+     * @param source      the new source <abbr>CRS</abbr>.
+     * @param target      the new target <abbr>CRS</abbr>.
      * @param factory     the factory to use for creating a transform from the 
parameters or for performing axis changes.
      */
     private DefaultConversion(final Conversion definition,
+                              final boolean normalized,
                               final CoordinateReferenceSystem source,
                               final CoordinateReferenceSystem target,
                               final MathTransformFactory factory) throws 
FactoryException
@@ -255,7 +242,7 @@ public class DefaultConversion extends 
AbstractSingleOperation implements Conver
                 final var context = (MathTransformProvider.Context) builder;
                 setParameterValues(context.getCompletedParameters(), new 
HashMap<>(context.getContextualParameters()));
             }
-        } else {
+        } else if (normalized) {
             /*
              * If the user specified explicitly a MathTransform, we may still 
need to swap or scale axes.
              * If this conversion is a defining conversion (which is usually 
the case when creating a new
@@ -337,19 +324,31 @@ public class DefaultConversion extends 
AbstractSingleOperation implements Conver
         return Conversion.class;
     }
 
+    /**
+     * Returns {@code true} if this conversion is defined between a pair of 
normalized <abbr>CRS</abbr>s.
+     * In such case, the conversion needs to be completed by a call to {@link 
#specialize specialize(…)}.
+     *
+     * @return whether this conversion is defined between a pair of normalized 
<abbr>CRS</abbr>s.
+     */
+    boolean normalized() {
+        return true;        // Ignored if this conversion has source and 
target CRSs.
+    }
+
     /**
      * Returns a specialization of this conversion with non-null 
<abbr>CRS</abbr>s.
-     * This {@code specialize(…)} method is typically invoked on {@linkplain 
#DefaultConversion(Map,
-     * OperationMethod, MathTransform, ParameterValueGroup) defining 
conversion} instances,
+     * This method is typically invoked on {@linkplain DefiningConversion 
defining conversion} instances,
      * when more information become available about the conversion to create.
+     * But this method can also be invoked on a fully defined conversion if 
the caller wants a conversion
+     * with slightly different source or target <abbr>CRS</abbr>. In the 
latter case, the changes in the
+     * <abbr>CRS</abbr>s should be limited to axis order and units of 
measurement.
      *
-     * @param  sourceCRS  the source CRS.
-     * @param  targetCRS  the target CRS.
+     * @param  sourceCRS  the source <abbr>CRS</abbr>.
+     * @param  targetCRS  the target <abbr>CRS</abbr>.
      * @param  factory    the factory to use for creating a transform from the 
parameters
      *         or for performing axis changes, or {@code null} for the default 
factory.
      * @return conversion which declares the given <abbr>CRS</abbr>s as the 
source and target.
-     * @throws MismatchedDatumException if the given CRS do not use the same 
datum as the source and target CRS
-     *         of this conversion.
+     * @throws MismatchedDatumException if the given <abbr>CRS</abbr>s do not 
use the same datum
+     *         as the source and target <abbr>CRS</abbr>s of this conversion.
      * @throws FactoryException if the creation of a {@link MathTransform} 
from the {@linkplain #getParameterValues()
      *         parameter values}, or a {@linkplain 
CoordinateSystems#swapAndScaleAxes change of axis order or units}
      *         failed.
@@ -391,7 +390,7 @@ public class DefaultConversion extends 
AbstractSingleOperation implements Conver
         if (factory == null) {
             factory = DefaultMathTransformFactory.provider();
         }
-        return new DefaultConversion(this, sourceCRS, targetCRS, factory);
+        return new DefaultConversion(this, normalized(), sourceCRS, targetCRS, 
factory);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
index bd6f8f728f..99a1fb9c27 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java
@@ -349,7 +349,7 @@ public class DefaultCoordinateOperationFactory extends 
AbstractFactory implement
     {
         final Conversion conversion;
         try {
-            conversion = new DefaultConversion(properties, method, null, 
parameters);
+            conversion = new DefiningConversion(properties, method, null, 
parameters);
         } catch (IllegalArgumentException exception) {
             throw new 
InvalidGeodeticParameterException(exception.getLocalizedMessage(), exception);
         }
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefiningConversion.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefiningConversion.java
new file mode 100644
index 0000000000..3cc74d1c78
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefiningConversion.java
@@ -0,0 +1,163 @@
+/*
+ * 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.Map;
+import jakarta.xml.bind.annotation.XmlTransient;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.referencing.crs.DerivedCRS;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.referencing.cs.AxesConvention;
+import org.apache.sis.referencing.internal.Resources;
+import 
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
+import org.apache.sis.util.collection.Containers;
+
+
+/**
+ * Conversion used as a carrier of parameters (without <abbr>CRS</abbr>s) for 
defining a final conversion.
+ * Defining conversions are used during the construction of {@linkplain 
DerivedCRS derived <abbr>CRS</abbr>},
+ * in which cases the source and target <abbr>CRS</abbr> are provided by the 
derived <abbr>CRS</abbr> itself.
+ * When those <abbr>CRS</abbr>s become available, the {@link #specialize 
specialize(…)} method can be invoked
+ * for {@linkplain DefaultMathTransformFactory#builder creating a math 
transform from the parameters}
+ * and assign the source and target <abbr>CRS</abbr> to the final conversion.
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @version 1.7
+ * @since   1.7
+ */
+@XmlTransient
+public class DefiningConversion extends DefaultConversion {
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = 901299137419800444L;
+
+    /**
+     * Key for a property specifying whether the conversion is fully-defined 
or defines only the part between
+     * normalized <abbr>CRS</abbr>s. The associated value shall be an instance 
of {@link Boolean}.
+     * Possible values are:
+     *
+     * <ul class="verbose">
+     *   <li>{@link Boolean#TRUE} means that the conversion is defined between 
a pair of <abbr>CRS</abbr>s
+     *     normalized in the sense of {@link AxesConvention#NORMALIZED}: the 
source and target coordinate
+     *     systems are right-handed and use predetermined units of measurement 
such as degrees and metres.
+     *     Such {@code DefiningConversion} may need to be completed with 
change of units and axis order.
+     *     These changes can be applied by {@link #specialize 
DefaultConversion.specialize(…)}.</li>
+     *   <li>{@link Boolean#FALSE} means that the conversion is already 
fully-defined,
+     *     including any change of units or axis order that may be required.
+     *     No conversion step will be added.</li>
+     * </ul>
+     *
+     * The default value is {@link Boolean#TRUE}.
+     *
+     * @see #normalized()
+     * @see AxesConvention#NORMALIZED
+     */
+    public static final String NORMALIZED_KEY = "normalized";
+
+    /**
+     * Whether this defining conversion provides a normalized transform.
+     * If {@code true}, then an adjustment for axis directions and units of 
measurement will need to be
+     * added when the source and target <abbr>CRS</abbr> will become known. If 
{@code false}, then this
+     * defining conversion shall provide the fully-defined transform and no 
adjustments will be added.
+     */
+    private final boolean normalized;
+
+    /**
+     * Creates a defining conversion from the given transform and/or 
parameters.
+     * This conversion has no source and target <abbr>CRS</abbr> since those 
elements
+     * are usually unknown at <i>defining conversion</i> construction time.
+     * The source and target <abbr>CRS</abbr> will become known later,
+     * at the {@linkplain DerivedCRS derived <abbr>CRS</abbr>} construction 
time.
+     *
+     * <p>The {@code properties} map given in argument follows the same rules 
as for the
+     * {@linkplain DefaultConversion#DefaultConversion(Map, 
CoordinateReferenceSystem, CoordinateReferenceSystem,
+     * CoordinateReferenceSystem, OperationMethod, MathTransform) parent 
constructor},
+     * with the addition of the following properties:</p>
+     *
+     * <table class="sis">
+     *   <caption>Additional properties</caption>
+     *   <tr>
+     *     <th>Property name</th>
+     *     <th>Value type</th>
+     *     <th>Returned by</th>
+     *   </tr><tr>
+     *     <td>{@value #CONVERSION_COMPLETION_KEY}</td>
+     *     <td>{@link Boolean}</td>
+     *     <td>{@link #normalized()}</td>
+     *   </tr>
+     * </table>
+     *
+     * <h4>Transform and parameters arguments</h4>
+     * At least one of the {@code transform} or {@code parameters} argument 
must be non-null.
+     * If the caller supplies a {@code transform} argument, then by default it 
shall be a transform expecting
+     * {@linkplain AxesConvention#NORMALIZED normalized} input coordinates and 
producing normalized output coordinates
+     * (see {@link AxesConvention} for more information about what Apache 
<abbr>SIS</abbr> means by "normalized").
+     * This default behavior can be disabled by setting the {@value 
#NORMALIZED_KEY} key to {@code false}.
+     *
+     * <p>If the caller cannot yet supply a {@code MathTransform}, then it 
shall supply the parameter values needed
+     * for creating that transform, with the possible omission of {@code 
"semi_major"} and {@code "semi_minor"} values.
+     * The semi-major and semi-minor parameter values will be set 
automatically when the
+     * {@link #specialize specialize(…)} method will be invoked.</p>
+     *
+     * <p>If both the {@code transform} and {@code parameters} arguments are 
non-null, then the latter should describe
+     * the parameters used for creating the transform. Those parameters will 
be stored for information purpose and can
+     * be given back by the {@link #getParameterValues()} method.</p>
+     *
+     * @param properties  the properties to be given to the identified object.
+     * @param method      the operation method.
+     * @param transform   transform from positions in the source CRS to 
positions in the target CRS, or {@code null}.
+     * @param parameters  the {@code transform} parameter values, or {@code 
null}.
+     */
+    @SuppressWarnings("this-escape")    // False positive.
+    public DefiningConversion(final Map<String,?>       properties,
+                              final OperationMethod     method,
+                              final MathTransform       transform,
+                              final ParameterValueGroup parameters)
+    {
+        super(properties, method);
+        this.transform = transform;
+        if (transform == null && parameters == null) {
+            throw new 
IllegalArgumentException(Resources.forProperties(properties)
+                    .getString(Resources.Keys.UnspecifiedParameterValues));
+        }
+        normalized = !Boolean.FALSE.equals(Containers.property(properties, 
NORMALIZED_KEY, Boolean.class));
+        setParameterValues(parameters, null);
+        checkDimensions(properties);
+    }
+
+    /**
+     * Returns {@code true} if this conversion is defined between a pair of 
normalized <abbr>CRS</abbr>s.
+     * In such case, the source and target coordinate systems are right-handed 
and use predetermined units
+     * of measurement such as degrees and metres. Such conversion needs to be 
completed by a call to
+     * {@link #specialize specialize(…)}.
+     *
+     * <p>If this method returns {@code false}, then this {@code 
DefiningConversion} defines fully the conversion
+     * and no conversion step should be added.</p>
+     *
+     * @return whether this conversion is defined between a pair of normalized 
<abbr>CRS</abbr>s.
+     *
+     * @see #NORMALIZED_KEY
+     * @see AxesConvention#NORMALIZED
+     */
+    @Override
+    public boolean normalized() {
+        return normalized;
+    }
+}
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java
index 6209578b96..85163e3725 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java
@@ -40,7 +40,7 @@ import org.opengis.referencing.operation.Conversion;
 import org.opengis.parameter.ParameterValueGroup;
 import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.MultiRegisterOperations;
-import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.DefiningConversion;
 import 
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
 import org.apache.sis.io.wkt.Convention;
 import org.apache.sis.measure.Units;
@@ -61,6 +61,7 @@ import static 
org.apache.sis.test.Assertions.assertMessageContains;
  *
  * @author  Cédric Briançon (Geomatys)
  */
+@SuppressWarnings("exports")
 @ExtendWith(FailureDetailsReporter.class)
 public final class GeodeticObjectFactoryTest extends ObjectFactoryTest {
     /**
@@ -204,7 +205,7 @@ public final class GeodeticObjectFactoryTest extends 
ObjectFactoryTest {
         parameters.parameter("latitude_of_origin").setValue(     -2);
         parameters.parameter("false_easting")     .setValue( 400000);
         parameters.parameter("false_northing")    .setValue(-100000);
-        projection = new DefaultConversion(name("GBN grid"), method, null, 
parameters);
+        projection = new DefiningConversion(name("GBN grid"), method, null, 
parameters);
         /*
          * Projected coordinate reference system
          */
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultConversionTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultConversionTest.java
index eac21492dd..507675ce09 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultConversionTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/DefaultConversionTest.java
@@ -223,7 +223,7 @@ public final class DefaultConversionTest extends TestCase {
     @Test
     public void testDefiningConversion() throws FactoryException {
         final DefaultConversion reference = createLongitudeRotation(true);
-        final DefaultConversion definingConversion = new DefaultConversion(
+        final DefaultConversion definingConversion = new DefiningConversion(
                 IdentifiedObjects.getProperties(reference),
                 reference.getMethod(),
                 reference.getMathTransform(),
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/HardCodedConversions.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/HardCodedConversions.java
index 1a960652dc..b5382307f6 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/HardCodedConversions.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/HardCodedConversions.java
@@ -100,7 +100,7 @@ public final class HardCodedConversions {
      * Creates a defining conversion of the given name with given parameter 
values.
      */
     private static DefaultConversion create(final String name, final 
OperationMethod method, final ParameterValueGroup pg) {
-        return new DefaultConversion(Map.of(OperationMethod.NAME_KEY, name), 
method, null, pg);
+        return new DefiningConversion(Map.of(OperationMethod.NAME_KEY, name), 
method, null, pg);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java
index 9cca92ced7..6929449861 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java
@@ -31,7 +31,7 @@ import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.parameter.ParameterValueGroup;
 import org.apache.sis.parameter.Parameterized;
 import org.apache.sis.referencing.crs.DefaultProjectedCRS;
-import org.apache.sis.referencing.operation.DefaultConversion;
+import org.apache.sis.referencing.operation.DefiningConversion;
 import org.apache.sis.referencing.operation.matrix.Matrix2;
 import org.apache.sis.referencing.operation.provider.Affine;
 import org.apache.sis.referencing.operation.provider.Mercator1SP;
@@ -290,7 +290,7 @@ public final class DefaultMathTransformFactoryTest extends 
TestCase {
              */
             final DefaultProjectedCRS crs = new DefaultProjectedCRS(dummyName,
                     HardCodedCRS.WGS84,
-                    new DefaultConversion(dummyName, method, mt, null),
+                    new DefiningConversion(dummyName, method, mt, null),
                     HardCodedCS.PROJECTED);
             final Conversion projection = crs.getConversionFromBase();
             assertSame(mt, projection.getMathTransform(), classification);

Reply via email to