This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push: new 7a049f9417 Update for the removal of the non-standard `org.opengis.referencing.operation.Projection` interface. 7a049f9417 is described below commit 7a049f9417379966bb8b6351c657657c916192d9 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Thu Apr 18 16:01:05 2024 +0200 Update for the removal of the non-standard `org.opengis.referencing.operation.Projection` interface. --- .../main/org/apache/sis/xml/XLink.java | 1 + .../sis/referencing/crs/AbstractDerivedCRS.java | 39 ++---- .../sis/referencing/crs/DefaultDerivedCRS.java | 11 +- .../sis/referencing/crs/DefaultProjectedCRS.java | 18 +-- .../sis/referencing/crs/ExplicitParameters.java | 2 +- .../referencing/factory/sql/AuthorityCodes.java | 45 ++----- .../referencing/factory/sql/EPSGDataAccess.java | 32 +---- .../sis/referencing/factory/sql/TableInfo.java | 4 +- .../operation/AbstractCoordinateOperation.java | 4 +- .../referencing/operation/DefaultConversion.java | 75 ++++------- .../DefaultCoordinateOperationFactory.java | 24 +--- .../operation/DefaultOperationMethod.java | 20 ++- .../operation/DefaultPassThroughOperation.java | 3 +- .../referencing/operation/DefaultProjection.java | 139 --------------------- .../apache/sis/referencing/operation/SubTypes.java | 75 ----------- .../operation/provider/AbstractProvider.java | 6 +- .../operation/provider/Equirectangular.java | 4 +- .../operation/provider/MapProjection.java | 4 +- .../operation/provider/PseudoPlateCarree.java | 3 - .../provider/ZonedTransverseMercator.java | 4 +- .../transform/DefaultMathTransformFactory.java | 12 +- .../referencing/factory/sql/EPSGFactoryTest.java | 11 +- .../operation/CoordinateOperationFinderTest.java | 3 +- .../operation/DefaultConversionTest.java | 21 ++-- .../operation/provider/ProvidersTest.java | 3 +- .../transform/DefaultMathTransformFactoryTest.java | 19 +-- .../report/CoordinateOperationMethods.java | 4 +- geoapi/snapshot | 2 +- 28 files changed, 116 insertions(+), 472 deletions(-) diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/XLink.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/XLink.java index e0ad0e8901..b5cf1d8592 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/XLink.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/XLink.java @@ -398,6 +398,7 @@ public class XLink implements Serializable { if (hashCode != 0) { throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1, "XLink")); } + @SuppressWarnings("LocalVariableHidesMemberVariable") final Type type = this.type; if (type != null) { if (value != null) { diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractDerivedCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractDerivedCRS.java index e27652217b..7b2f7e7655 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractDerivedCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractDerivedCRS.java @@ -51,8 +51,6 @@ import org.apache.sis.util.resources.Errors; * A coordinate reference system that is defined by its coordinate conversion from another CRS. * * @author Martin Desruisseaux (IRD, Geomatys) - * - * @param <C> the conversion type, either {@code Conversion} or {@code Projection}. */ @XmlType(name = "AbstractGeneralDerivedCRSType") @XmlRootElement(name = "AbstractGeneralDerivedCRS") @@ -60,7 +58,7 @@ import org.apache.sis.util.resources.Errors; DefaultDerivedCRS.class, DefaultProjectedCRS.class }) -abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS implements DerivedCRS { +abstract class AbstractDerivedCRS extends AbstractCRS implements DerivedCRS { /** * Serial number for inter-operability with different versions. */ @@ -76,7 +74,7 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl * @see #getConversionFromBase() */ @SuppressWarnings("serial") // Most SIS implementations are serializable. - private C conversionFromBase; + private Conversion conversionFromBase; /** * Creates a derived CRS from a defining conversion. @@ -111,7 +109,7 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl * Creates a new CRS derived from the specified one, but with different axis order or unit. * This is for implementing the {@link #createSameType(AbstractCS)} method only. */ - AbstractDerivedCRS(final AbstractDerivedCRS<C> original, final AbstractCS derivedCS) { + AbstractDerivedCRS(final AbstractDerivedCRS original, final AbstractCS derivedCS) { super(original, null, derivedCS); final Conversion conversion = original.conversionFromBase; conversionFromBase = createConversionFromBase(null, (SingleCRS) conversion.getSourceCRS(), conversion); @@ -134,8 +132,8 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl ArgumentChecks.ensureNonNull("baseCRS", baseCRS); ArgumentChecks.ensureNonNull("method", method); ArgumentChecks.ensureNonNull("baseToDerived", baseToDerived); - conversionFromBase = (C) new DefaultConversion( // Cast to (C) is valid only for DefaultDerivedCRS. - ConversionKeys.unprefix(properties), baseCRS, this, interpolationCRS, method, baseToDerived); + conversionFromBase = new DefaultConversion(ConversionKeys.unprefix(properties), + baseCRS, this, interpolationCRS, method, baseToDerived); } /** @@ -161,13 +159,13 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl * instance is advanced enough for allowing the {@code getCoordinateSystem()} method to execute. * Subclasses should make their {@code getCoordinateSystem()} method final for better guarantees.</p> */ - private C createConversionFromBase(final Map<String,?> properties, final SingleCRS baseCRS, final Conversion conversion) { + private Conversion createConversionFromBase(final Map<String,?> properties, final SingleCRS baseCRS, final Conversion conversion) { MathTransformFactory factory = null; if (properties != null) { factory = (MathTransformFactory) properties.get(ReferencingFactoryContainer.MT_FACTORY); } try { - return DefaultConversion.castOrCopy(conversion).specialize(getConversionType(), baseCRS, this, factory); + return DefaultConversion.castOrCopy(conversion).specialize(baseCRS, this, factory); } catch (FactoryException e) { throw new IllegalArgumentException(Errors.forProperties(properties).getString( Errors.Keys.IllegalArgumentValue_2, "conversion", conversion.getName()), e); @@ -175,22 +173,7 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl } /** - * Returns the type of conversion associated to this {@code AbstractDerivedCRS}. - * - * <p><b>WARNING:</b> this method is invoked (indirectly) at construction time. - * Consequently, it shall return a constant value - this method is not allowed to - * depend on the object state.</p> - */ - abstract Class<C> getConversionType(); - - /** - * Returns the GeoAPI interface implemented by this class. - */ - @Override - public abstract Class<? extends DerivedCRS> getInterface(); - - /** - * Returns the datum of the {@linkplain #getBaseCRS() base CRS}. + * Returns the datum of the base CRS. * * @return the datum of the base CRS. */ @@ -198,13 +181,13 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl public abstract Datum getDatum(); /** - * Returns the conversion from the {@linkplain #getBaseCRS() base CRS} to this CRS. + * Returns the conversion from the base CRS to this CRS. * * @return the conversion to this CRS. */ @Override @XmlElement(name = "conversion", required = true) - public C getConversionFromBase() { + public Conversion getConversionFromBase() { return conversionFromBase; } @@ -297,7 +280,7 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl * At this state, the given conversion has null {@code sourceCRS} and {@code targetCRS}. * Those CRS will be set later, in {@link #afterUnmarshal(Unmarshaller, Object)}. */ - private void setConversionFromBase(final C conversion) { + private void setConversionFromBase(final Conversion conversion) { if (conversionFromBase == null) { conversionFromBase = conversion; } else { 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 631d67866d..832a631500 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 @@ -105,7 +105,7 @@ import org.opengis.referencing.cs.ParametricCS; "coordinateSystem" }) @XmlRootElement(name = "DerivedCRS") -public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements DerivedCRS { +public class DefaultDerivedCRS extends AbstractDerivedCRS implements DerivedCRS { /** * Serial number for inter-operability with different versions. */ @@ -380,15 +380,6 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements } } - /** - * Returns the type of conversion associated to this {@code DefaultDerivedCRS}. - * Must be a hard-coded, constant value (not dependent on object state). - */ - @Override - final Class<Conversion> getConversionType() { - return Conversion.class; - } - /** * Returns the GeoAPI interface implemented by this class. * The SIS implementation returns {@code DerivedCRS.class}. diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java index 0a69f68c58..bca03ade69 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java @@ -28,7 +28,6 @@ import org.opengis.referencing.cs.CartesianCS; import org.opengis.referencing.cs.CoordinateSystem; // For javadoc import org.opengis.referencing.datum.GeodeticDatum; import org.opengis.referencing.operation.Conversion; -import org.opengis.referencing.operation.Projection; import org.opengis.geometry.MismatchedDimensionException; import org.apache.sis.referencing.cs.AxesConvention; import org.apache.sis.referencing.cs.AbstractCS; @@ -72,7 +71,7 @@ import org.apache.sis.util.Workaround; "coordinateSystem" }) @XmlRootElement(name = "ProjectedCRS") -public class DefaultProjectedCRS extends AbstractDerivedCRS<Projection> implements ProjectedCRS { +public class DefaultProjectedCRS extends AbstractDerivedCRS implements ProjectedCRS { /** * Serial number for inter-operability with different versions. */ @@ -191,15 +190,6 @@ public class DefaultProjectedCRS extends AbstractDerivedCRS<Projection> implemen ? (DefaultProjectedCRS) object : new DefaultProjectedCRS(object); } - /** - * Returns the type of conversion associated to this {@code DefaultProjectedCRS}. - * Must be a hard-coded, constant value (not dependent on object state). - */ - @Override - final Class<Projection> getConversionType() { - return Projection.class; - } - /** * Returns the GeoAPI interface implemented by this class. * The SIS implementation returns {@code ProjectedCRS.class}. @@ -236,8 +226,8 @@ public class DefaultProjectedCRS extends AbstractDerivedCRS<Projection> implemen */ @Override public GeodeticCRS getBaseCRS() { - final Projection projection = super.getConversionFromBase(); - return (projection != null) ? projection.getSourceCRS() : null; + final Conversion projection = super.getConversionFromBase(); + return (projection != null) ? (GeodeticCRS) projection.getSourceCRS() : null; } /** @@ -257,7 +247,7 @@ public class DefaultProjectedCRS extends AbstractDerivedCRS<Projection> implemen * @return the map projection from base CRS to this CRS. */ @Override - public Projection getConversionFromBase() { + public Conversion getConversionFromBase() { return super.getConversionFromBase(); } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java index 5984372e29..65a7624e86 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/ExplicitParameters.java @@ -59,7 +59,7 @@ final class ExplicitParameters extends FormattableObject { /** * Creates a new temporary {@code Conversion} elements for the parameters of the given CRS. */ - ExplicitParameters(final AbstractDerivedCRS<?> crs, final String keyword) { + ExplicitParameters(final AbstractDerivedCRS crs, final String keyword) { conversion = crs.getConversionFromBase(); final Datum datum = crs.getDatum(); ellipsoid = (datum instanceof GeodeticDatum) ? ((GeodeticDatum) datum).getEllipsoid() : null; diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/AuthorityCodes.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/AuthorityCodes.java index 9fd9585b36..6dab326502 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/AuthorityCodes.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/AuthorityCodes.java @@ -24,7 +24,6 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.PreparedStatement; import java.sql.Statement; -import org.opengis.referencing.operation.Projection; import org.apache.sis.util.collection.BackingStoreException; import org.apache.sis.util.collection.IntegerList; import org.apache.sis.util.privy.AbstractMap; @@ -79,11 +78,6 @@ final class AuthorityCodes extends AbstractMap<String,String> implements Seriali */ final Class<?> type; - /** - * {@code true} if {@link #type} is assignable to {@link Projection}. - */ - private final transient boolean isProjection; - /** * The SQL commands that this {@code AuthorityCodes} may need to execute. * In this array: @@ -165,7 +159,6 @@ final class AuthorityCodes extends AbstractMap<String,String> implements Seriali * Other information opportunistically computed from above search. */ this.type = tableType; - isProjection = Projection.class.isAssignableFrom(tableType); } /** @@ -177,14 +170,6 @@ final class AuthorityCodes extends AbstractMap<String,String> implements Seriali return new CloseableReference(this, factory, statements); } - /** - * Returns {@code true} if the specified code should be included in this map. - */ - private boolean filter(final int code) throws SQLException { - assert Thread.holdsLock(factory); - return !isProjection || factory.isProjection(code); - } - /** * Returns the code at the given index, or -1 if the index is out of bounds. * @@ -216,10 +201,8 @@ final class AuthorityCodes extends AbstractMap<String,String> implements Seriali return -1; } code = r.getInt(1); - if (filter(code)) { - codes.addInt(code); - more--; - } + codes.addInt(code); + more--; } while (more >= 0); } } @@ -273,19 +256,17 @@ final class AuthorityCodes extends AbstractMap<String,String> implements Seriali } try { synchronized (factory) { - if (filter(n)) { - PreparedStatement statement = (PreparedStatement) statements[ONE]; - if (statement == null) { - statements[ONE] = statement = factory.connection.prepareStatement(sql[ONE]); - sql[ONE] = null; // Not needed anymore. - } - statement.setInt(1, n); - try (ResultSet r = statement.executeQuery()) { - while (r.next()) { - String name = r.getString(1); - if (name != null) { - return name; - } + PreparedStatement statement = (PreparedStatement) statements[ONE]; + if (statement == null) { + statements[ONE] = statement = factory.connection.prepareStatement(sql[ONE]); + sql[ONE] = null; // Not needed anymore. + } + statement.setInt(1, n); + try (ResultSet r = statement.executeQuery()) { + while (r.next()) { + String name = r.getString(1); + if (name != null) { + return name; } } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java index 30a53e2475..ba8f9caf19 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java @@ -285,14 +285,6 @@ public class EPSGDataAccess extends GeodeticAuthorityFactory implements CRSAutho */ private final Map<Integer,AxisName> axisNames = new HashMap<>(); - /** - * Cache for whether conversions are projections. This service is not provided by {@code ConcurrentAuthorityFactory} - * since the check for conversion type is used internally in this class. - * - * @see #isProjection(Integer) - */ - private final Map<Integer,Boolean> isProjection = new HashMap<>(); - /** * Cache of naming systems other than EPSG. There is usually few of them (at most 15). * This is used for aliases. @@ -1447,7 +1439,7 @@ codes: for (int i=0; i<codes.length; i++) { } catch (ClassCastException e) { // Should never happen in a well-formed EPSG database. // If happen anyway, the ClassCastException cause will give more hints than just the message. - throw (NoSuchAuthorityCodeException) noSuchAuthorityCode(Projection.class, opCode).initCause(e); + throw (NoSuchAuthorityCodeException) noSuchAuthorityCode(Conversion.class, opCode).initCause(e); } final CoordinateReferenceSystem baseCRS; final boolean suspendParamChecks; @@ -3128,28 +3120,6 @@ next: while (r.next()) { } } - /** - * Returns {@code true} if the {@link CoordinateOperation} for the specified code is a {@link Projection}. - * The caller must have verified that the designed operation is a {@link Conversion} before to invoke this method. - * - * @throws SQLException if an error occurred while querying the database. - */ - final boolean isProjection(final Integer code) throws SQLException { - Boolean projection = isProjection.get(code); - if (projection == null) { - try (ResultSet result = executeQuery("isProjection", - "SELECT COORD_REF_SYS_CODE" + - " FROM [Coordinate Reference System]" + - " WHERE PROJECTION_CONV_CODE = ?" + - " AND CAST(COORD_REF_SYS_KIND AS " + TableInfo.ENUM_REPLACEMENT + ") LIKE 'projected%'", code)) - { - projection = result.next(); - } - isProjection.put(code, projection); - } - return projection; - } - /** * Sorts an array of codes in preference order. This method orders pairwise the codes according the information * provided in the supersession table. If the same object is superseded by more than one object, then the most diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/TableInfo.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/TableInfo.java index c7ed64c976..61f0df5aab 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/TableInfo.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/TableInfo.java @@ -139,8 +139,8 @@ final class TableInfo { "COORD_OP_CODE", "COORD_OP_NAME", "COORD_OP_TYPE", - new Class<?>[] { Projection.class, Conversion.class, Transformation.class}, - new String[] {"conversion", "conversion", "transformation"}, + new Class<?>[] { Conversion.class, Transformation.class}, + new String[] {"conversion", "transformation"}, "SHOW_OPERATION"), // Note: Projection is handled in a special way. diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java index 9bd95fe8b8..14326610b7 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java @@ -436,9 +436,9 @@ check: for (int isTarget=0; ; isTarget++) { // 0 == source check; 1 * <ul> * <li>If the given object is {@code null}, then this method returns {@code null}.</li> * <li>Otherwise if the given object is an instance of - * {@link org.opengis.referencing.operation.Transformation}, * {@link org.opengis.referencing.operation.Conversion}, - * {@link org.opengis.referencing.operation.Projection}, + * {@link org.opengis.referencing.operation.Transformation}, + * {@link org.opengis.referencing.operation.PointMotionOperation}, * {@link org.opengis.referencing.operation.PassThroughOperation} or * {@link org.opengis.referencing.operation.ConcatenatedOperation}, * then this method delegates to the {@code castOrCopy(…)} method of the corresponding SIS subclass. 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 1e76500102..90cf08b7bd 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 @@ -23,7 +23,6 @@ import javax.measure.IncommensurableException; import org.opengis.util.FactoryException; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.operation.Conversion; -import org.opengis.referencing.operation.Projection; import org.opengis.referencing.operation.OperationMethod; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; @@ -64,8 +63,7 @@ import org.apache.sis.util.resources.Errors; * 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#createParameterizedTransform creating a math transform from the parameters}, - * instantiate a new {@code Conversion} of a more specific type ({@link Projection}) if relevant, + * {@linkplain DefaultMathTransformFactory#createParameterizedTransform creating a math transform from the parameters} * and assign the source and target CRS to it.</p> * * <h2>Immutability and thread safety</h2> @@ -130,8 +128,8 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver * {@linkplain org.apache.sis.referencing.crs.DefaultDerivedCRS derived CRS} construction.</p> * * <h4>Example</h4> - * 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 + * 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 * temporal datum} definition. However, such operation does not have all the accuracy issues of transformations * between geodetic datum (empirically determined, over-determined systems, stochastic nature of the parameters). @@ -219,13 +217,11 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver * @param source the new source CRS. * @param target the new target CRS. * @param factory the factory to use for creating a transform from the parameters or for performing axis changes. - * @param actual an array of length 1 where to store the actual operation method used by the math transform factory. */ - DefaultConversion(final Conversion definition, - final CoordinateReferenceSystem source, - final CoordinateReferenceSystem target, - final MathTransformFactory factory, - final OperationMethod[] actual) throws FactoryException + private DefaultConversion(final Conversion definition, + final CoordinateReferenceSystem source, + final CoordinateReferenceSystem target, + final MathTransformFactory factory) throws FactoryException { super(definition); int interpDim = ReferencingUtilities.getDimension(super.getInterpolationCRS().orElse(null)); @@ -254,7 +250,6 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver context = ReferencingUtilities.createTransformContext(source, target); } transform = ((DefaultMathTransformFactory) factory).createParameterizedTransform(parameters, context); - actual[0] = context.getMethodUsed(); setParameterValues(context.getCompletedParameters(), context.getContextualParameters()); } else { /* @@ -263,7 +258,6 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver * the code should work anyway. */ transform = factory.createBaseToDerived(source, parameters, target.getCoordinateSystem()); - actual[0] = factory.getLastMethodUsed(); } } else { /* @@ -273,7 +267,7 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver * method for this job. */ if (sourceCRS == null && targetCRS == null && factory instanceof DefaultMathTransformFactory) { - final DefaultMathTransformFactory.Context context = new DefaultMathTransformFactory.Context(); + final var context = new DefaultMathTransformFactory.Context(); context.setSource(source.getCoordinateSystem()); context.setTarget(target.getCoordinateSystem()); // See comment on the other setTarget(…) call. transform = ((DefaultMathTransformFactory) factory).swapAndScaleAxes(transform, context); @@ -333,11 +327,7 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver if (object == null || object instanceof DefaultConversion) { return (DefaultConversion) object; } - if (object instanceof Projection) { - return new DefaultProjection((Projection) object); - } else { - return new DefaultConversion(object); - } + return new DefaultConversion(object); } /** @@ -352,32 +342,16 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver } /** - * Returns a specialization of this conversion with a more specific type and non-null <abbr>CRS</abbr>s. + * 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, * when more information become available about the conversion to create. * - * <p>The given {@code baseType} argument can be one of the following values:</p> - * <ul> - * <li><code>{@linkplain Conversion}.class</code></li> - * <li><code>{@linkplain Projection}.class</code></li> - * </ul> - * - * This {@code specialize(…)} method returns a conversion which implement at least the given {@code baseType} - * interface, but may also implement a more specific interface if {@code specialize(…)} has been able to infer - * the type from the {@linkplain #getMethod() operation method}. - * The list of interfaces supported by this method may change in any version of Apache SIS. - * - * @param <T> compile-time type of the {@code baseType} argument. - * @param baseType the base GeoAPI interface to be implemented by the conversion to return. * @param sourceCRS the source CRS. * @param targetCRS the target CRS. - * @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 of the given type which declares the given <abbr>CRS</abbr>s as the source and target. - * @throws ClassCastException if a contradiction is found between the given {@code baseType}, - * the defining {@linkplain DefaultConversion#getInterface() conversion type} and - * the {@linkplain DefaultOperationMethod#getOperationType() method operation type}. + * @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 FactoryException if the creation of a {@link MathTransform} from the {@linkplain #getParameterValues() @@ -385,16 +359,17 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver * failed. * * @see DefaultMathTransformFactory#createParameterizedTransform(ParameterValueGroup, DefaultMathTransformFactory.Context) + * + * @since 1.5 */ - public <T extends Conversion> T specialize(final Class<T> baseType, - final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS, - MathTransformFactory factory) throws FactoryException + public Conversion specialize(final CoordinateReferenceSystem sourceCRS, + final CoordinateReferenceSystem targetCRS, + MathTransformFactory factory) throws FactoryException { - ArgumentChecks.ensureNonNull("baseType", baseType); ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS); ArgumentChecks.ensureNonNull("targetCRS", targetCRS); /* - * Conceptual consistency check: verify that the new CRS use the same datum as the previous ones, + * Conceptual consistency check: verify that the new CRSs use the same datum as the previous ones, * since the purpose of this method is not to apply datum changes. Datum changes are the purpose of * a dedicated kind of operations, namely Transformation. */ @@ -411,14 +386,18 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver * datum for source and target CRS, since DerivedCRS and ProjectedCRS are expected to have the same * datum than their source CRS. */ - if (super.getTargetCRS() != null) { - ensureCompatibleDatum("targetCRS", sourceCRS, super.getTargetCRS()); - } + ensureCompatibleDatum("targetCRS", sourceCRS, super.getTargetCRS()); + } + if (super.getSourceCRS() == sourceCRS && + super.getTargetCRS() == targetCRS && + super.getMathTransform() != null) + { + return this; } if (factory == null) { factory = DefaultMathTransformFactory.provider(); } - return SubTypes.create(baseType, this, sourceCRS, targetCRS, factory); + return new DefaultConversion(this, 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 bbf45d2eef..65fbd9cc84 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 @@ -28,8 +28,6 @@ import org.opengis.referencing.AuthorityFactory; import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.operation.*; import org.opengis.referencing.crs.CoordinateReferenceSystem; -import org.opengis.referencing.crs.GeodeticCRS; -import org.opengis.referencing.crs.ProjectedCRS; import org.opengis.referencing.crs.SingleCRS; import org.opengis.referencing.crs.CRSFactory; import org.opengis.referencing.cs.CSFactory; @@ -553,22 +551,12 @@ next: for (int i=components.size(); --i >= 0;) { * * - If the two CRS uses the same datum (ignoring metadata), assume that we have a Conversion. * - Otherwise we have a datum change, which implies that we have a Transformation. - * - * In the case of Conversion, we can specialize one step more if the conversion is going from a geographic CRS - * to a projected CRS. It may seems that we should check if ProjectedCRS.getBaseCRS() is equal (ignoring meta - * data) to source CRS. But we already checked the datum, which is the important part. The axis order and unit - * could be different, which we want to allow. */ if (baseType == SingleOperation.class) { if (isConversion(sourceCRS, targetCRS)) { - if (interpolationCRS == null && sourceCRS instanceof GeodeticCRS - && targetCRS instanceof ProjectedCRS) - { - baseType = Projection.class; - } else { - baseType = Conversion.class; - } + baseType = Conversion.class; } else { + // TODO: handle point motion operation. baseType = Transformation.class; } } @@ -585,14 +573,6 @@ next: for (int i=components.size(); --i >= 0;) { final AbstractSingleOperation op; if (Transformation.class.isAssignableFrom(baseType)) { op = new DefaultTransformation(properties, sourceCRS, targetCRS, interpolationCRS, method, transform); - } else if (Projection.class.isAssignableFrom(baseType)) { - ArgumentChecks.ensureCanCast("sourceCRS", GeodeticCRS .class, sourceCRS); - ArgumentChecks.ensureCanCast("targetCRS", ProjectedCRS.class, targetCRS); - if (interpolationCRS != null) { - throw new IllegalArgumentException(Errors.format( - Errors.Keys.ForbiddenAttribute_2, "interpolationCRS", baseType)); - } - op = new DefaultProjection(properties, (GeodeticCRS) sourceCRS, (ProjectedCRS) targetCRS, method, transform); } else if (Conversion.class.isAssignableFrom(baseType)) { op = new DefaultConversion(properties, sourceCRS, targetCRS, interpolationCRS, method, transform); } else { // See above comment about this last-resort fallback. diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultOperationMethod.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultOperationMethod.java index 60ace525eb..21375f03fb 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultOperationMethod.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultOperationMethod.java @@ -29,7 +29,6 @@ import org.opengis.metadata.citation.Citation; import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.operation.Formula; import org.opengis.referencing.operation.Conversion; -import org.opengis.referencing.operation.Projection; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.OperationMethod; import org.opengis.referencing.operation.SingleOperation; @@ -334,16 +333,15 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements * The base {@code CoordinateOperation} interface is usually one of the following subtypes: * * <ul class="verbose"> + * <li class="verbose">{@link org.opengis.referencing.operation.Conversion} + * if the coordinate operation is theoretically of infinite precision, ignoring the limitations of floating + * point arithmetic (including rounding errors) and the approximations implied by finite series expansions.</li> * <li>{@link org.opengis.referencing.operation.Transformation} * if the coordinate operation has some errors (typically of a few metres) because of the empirical process by * which the operation parameters were determined. Those errors do not depend on the floating point precision * or the accuracy of the implementation algorithm.</li> - * <li class="verbose">{@link org.opengis.referencing.operation.Conversion} - * if the coordinate operation is theoretically of infinite precision, ignoring the limitations of floating - * point arithmetic (including rounding errors) and the approximations implied by finite series expansions.</li> - * <li>{@link org.opengis.referencing.operation.Projection} - * if the coordinate operation is a conversion (as defined above) converting geodetic latitudes and longitudes - * to plane (map) coordinates.</li> + * <li>{@link org.opengis.referencing.operation.PointMotionOperation} + * if the coordinate operation applies changes due to the motion of points between two coordinate epochs.</li> * </ul> * * In case of doubt, {@code getOperationType()} can conservatively return the base type. @@ -523,8 +521,8 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements if (isWKT1) { /* * The WKT 1 keyword is "PROJECTION", which imply that the operation method should be of type - * org.opengis.referencing.operation.Projection. So strictly speaking only the first check in - * the following 'if' statement is relevant. + * org.opengis.referencing.operation.Conversion. So strictly speaking only the first check in + * the following 'if' statement is relevant, and we should also check CRS types. * * Unfortunately in many cases we do not know the operation type, because the method that we * invoked - getOperationType() - is not a standard OGC/ISO property, so this information is @@ -535,10 +533,10 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements * * In other words, the combination of those two checks exclude the following operation types: * Transformation, ConcatenatedOperation, PassThroughOperation, or any user-defined type that - * do not extend Projection. All other operation types are accepted. + * do not extend Conversion. All other operation types are accepted. */ final Class<? extends SingleOperation> type = getOperationType(); - if (Projection.class.isAssignableFrom(type) || type.isAssignableFrom(Projection.class)) { + if (Conversion.class.isAssignableFrom(type) || type.isAssignableFrom(Conversion.class)) { return WKTKeywords.Projection; } formatter.setInvalidWKT(this, null); diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java index a444daff6a..762e3283c4 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java @@ -370,6 +370,7 @@ public class DefaultPassThroughOperation extends AbstractCoordinateOperation imp * State validation. The `missing` string will be used in exception message * at the end of this method if a required component is reported missing. */ + @SuppressWarnings("LocalVariableHidesMemberVariable") final int[] modifiedCoordinates = this.modifiedCoordinates; FactoryException cause = null; String missing = "modifiedCoordinate"; @@ -393,7 +394,7 @@ public class DefaultPassThroughOperation extends AbstractCoordinateOperation imp if (sourceSub == null) sourceSub = CRS.selectDimensions(sourceCRS, modifiedCoordinates); if (targetSub == null) targetSub = CRS.selectDimensions(targetCRS, modifiedCoordinates); operation = DefaultConversion.castOrCopy((Conversion) operation) - .specialize(Conversion.class, sourceSub, targetSub, null); + .specialize(sourceSub, targetSub, null); subTransform = operation.getMathTransform(); } catch (FactoryException e) { cause = e; diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultProjection.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultProjection.java deleted file mode 100644 index 123b12a327..0000000000 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultProjection.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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.util.FactoryException; -import org.opengis.referencing.operation.Conversion; -import org.opengis.referencing.operation.Projection; -import org.opengis.referencing.operation.OperationMethod; -import org.opengis.referencing.operation.MathTransform; -import org.opengis.referencing.operation.MathTransformFactory; -import org.opengis.referencing.crs.GeodeticCRS; -import org.opengis.referencing.crs.ProjectedCRS; -import org.opengis.referencing.crs.CoordinateReferenceSystem; -import org.apache.sis.util.ArgumentChecks; - - -/** - * A conversion from (<var>longitude</var>, <var>latitude</var>) coordinates to Cartesian coordinates - * (<var>x</var>,<var>y</var>). - * - * <p>An unofficial list of projections and their parameters can be found - * <a href="http://geotiff.maptools.org/proj_list/">there</a>. - * Most projections expect the following parameters:</p> - * - * <ul> - * <li>{@code "central_meridian"} (default to 0), - * <li>{@code "latitude_of_origin"} (default to 0), - * <li>{@code "scale_factor"} (default to 1), - * <li>{@code "false_easting"} (default to 0) and - * <li>{@code "false_northing"} (default to 0). - * </ul> - * - * @author Martin Desruisseaux (IRD, Geomatys) - * - * @see org.apache.sis.referencing.crs.DefaultProjectedCRS - */ -@XmlTransient -final class DefaultProjection extends DefaultConversion implements Projection { - /** - * Serial number for inter-operability with different versions. - */ - private static final long serialVersionUID = -7176751851369816864L; - - /** - * Creates a projection from the given properties. - * - * @param properties the properties to be given to the identified object. - * @param sourceCRS the source CRS. - * @param targetCRS the target CRS. - * @param method the coordinate operation method. - * @param transform transform from positions in the source CRS to positions in the target CRS. - */ - public DefaultProjection(final Map<String,?> properties, - final GeodeticCRS sourceCRS, - final ProjectedCRS targetCRS, - final OperationMethod method, - final MathTransform transform) - { - super(properties, sourceCRS, targetCRS, null, method, transform); - } - - /** - * Creates a new projection 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. - * - * @param definition the defining conversion. - * @param sourceCRS the source CRS. - * @param targetCRS the target CRS. - * @param factory the factory to use for creating a transform from the parameters or for performing axis changes. - * @param actual an array of length 1 where to store the actual operation method used by the math transform factory. - * @throws IllegalArgumentException if the source or targe CRS is not of the requested type. - */ - DefaultProjection(final Conversion definition, - final CoordinateReferenceSystem sourceCRS, - final CoordinateReferenceSystem targetCRS, - final MathTransformFactory factory, - final OperationMethod[] actual) throws FactoryException - { - super(definition, sourceCRS, targetCRS, factory, actual); - ArgumentChecks.ensureCanCast("sourceCRS", GeodeticCRS.class, sourceCRS); - ArgumentChecks.ensureCanCast("targetCRS", ProjectedCRS.class, targetCRS); - } - - /** - * Creates a new coordinate operation with the same values as the specified one. - * This copy constructor provides a way to convert an arbitrary implementation - * into a SIS one, usually in order to leverage some implementation-specific API. - * - * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> - * - * @param operation the coordinate operation to copy. - */ - protected DefaultProjection(final Projection operation) { - super(operation); - } - - /** - * Returns the GeoAPI interface implemented by this class. - * - * @return the conversion interface implemented by this class. - */ - @Override - public Class<? extends Projection> getInterface() { - return Projection.class; - } - - /** - * Returns the source CRS, which must be geographic or {@code null}. - */ - @Override - public final GeodeticCRS getSourceCRS() { - return (GeodeticCRS) super.getSourceCRS(); - } - - /** - * Returns the target CRS, which must be projected or {@code null}. - */ - @Override - public final ProjectedCRS getTargetCRS() { - return (ProjectedCRS) super.getTargetCRS(); - } -} diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/SubTypes.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/SubTypes.java index c17d8883d6..d7e8ce8b8b 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/SubTypes.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/SubTypes.java @@ -16,10 +16,7 @@ */ package org.apache.sis.referencing.operation; -import org.opengis.util.FactoryException; import org.opengis.referencing.operation.*; -import org.opengis.referencing.crs.CoordinateReferenceSystem; -import org.apache.sis.referencing.AbstractIdentifiedObject; /** @@ -30,8 +27,6 @@ import org.apache.sis.referencing.AbstractIdentifiedObject; * <p>This class currently provides implementation for the following methods:</p> * <ul> * <li>{@link AbstractCoordinateOperation#castOrCopy(CoordinateOperation)}</li> - * <li>{@link DefaultConversion#castOrCopy(Conversion)}</li> - * <li>{@link DefaultConversion#specialize(Class, CoordinateReferenceSystem, CoordinateReferenceSystem, MathTransformFactory)}</li> * </ul> * * @author Martin Desruisseaux (Geomatys) @@ -76,74 +71,4 @@ final class SubTypes { } return new AbstractCoordinateOperation(object); } - - /** - * Returns a conversion from the specified defining conversion. - * The returned conversion will implement at least the {@code baseType} interface, but may implement - * a more specific GeoAPI interface if this method has been able to infer the type from the - * {@code conversion} argument. - * - * @param baseType the base GeoAPI interface to be implemented by the conversion to return. - * @param definition the defining conversion. - * @param sourceCRS the source CRS. - * @param targetCRS the target CRS. - * @param factory the factory to use for creating a transform from the parameters or for performing axis changes. - * @return the conversion of the given type between the given CRS. - * @throws ClassCastException if a contradiction is found between the given {@code baseType}, - * the defining {@linkplain DefaultConversion#getInterface() conversion type} and - * the {@linkplain DefaultOperationMethod#getOperationType() method operation type}. - */ - static <T extends Conversion> T create(final Class<T> baseType, Conversion definition, - final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS, - final MathTransformFactory factory) throws FactoryException - { - Class<? extends T> type = baseType; - if (definition instanceof AbstractIdentifiedObject) { - final Class<?> c = ((AbstractIdentifiedObject) definition).getInterface(); - if (!c.isAssignableFrom(baseType)) { // Do nothing if c is a parent type. - type = c.asSubclass(type); - } - } - final OperationMethod method = definition.getMethod(); - if (method instanceof DefaultOperationMethod) { - final Class<? extends SingleOperation> c = ((DefaultOperationMethod) method).getOperationType(); - if (!c.isAssignableFrom(baseType)) { // Do nothing if c is a parent type. - type = c.asSubclass(type); - } - } - Conversion conversion; - if (type.isInstance(definition) - && definition.getSourceCRS() == sourceCRS - && definition.getTargetCRS() == targetCRS - && definition.getMathTransform() != null) - { - conversion = definition; - } else { - final OperationMethod[] actual = new OperationMethod[1]; - boolean tryAgain; - do { - tryAgain = false; - if (Projection.class.isAssignableFrom(type)) { - conversion = new DefaultProjection(definition, sourceCRS, targetCRS, factory, actual); - } else { - conversion = new DefaultConversion(definition, sourceCRS, targetCRS, factory, actual); - } - /* - * The DefaultConversion constructor may have used MathTransformFactory for creating the actual - * MathTransform object. In such case, we can use the knownledge that the factory has about the - * coordinate operation for refining again the type of the object to be returned. - */ - final OperationMethod m = actual[0]; - if (m instanceof DefaultOperationMethod) { - final Class<?> t = ((DefaultOperationMethod) m).getOperationType(); - if (t != null && t != type && type.isAssignableFrom(t)) { - type = t.asSubclass(type); - definition = conversion; - tryAgain = true; - } - } - } while (tryAgain); - } - return type.cast(conversion); - } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/AbstractProvider.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/AbstractProvider.java index f7f6f19e72..761257b360 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/AbstractProvider.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/AbstractProvider.java @@ -60,9 +60,9 @@ public abstract class AbstractProvider extends DefaultOperationMethod implements /** * The base interface of the {@code CoordinateOperation} instances that use this method. * Value can be {@link org.opengis.referencing.operation.SingleOperation}, - * {@link org.opengis.referencing.operation.Transformation}, - * {@link org.opengis.referencing.operation.Conversion} or - * {@link org.opengis.referencing.operation.Projection}. + * {@link org.opengis.referencing.operation.Conversion}, + * {@link org.opengis.referencing.operation.Transformation} or + * {@link org.opengis.referencing.operation.PointMotionOperation}. * * @see #getOperationType() */ diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Equirectangular.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Equirectangular.java index 46ed35bf62..e0b023c68f 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Equirectangular.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Equirectangular.java @@ -25,7 +25,7 @@ import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.cs.CartesianCS; import org.opengis.referencing.cs.EllipsoidalCS; -import org.opengis.referencing.operation.Projection; +import org.opengis.referencing.operation.Conversion; import org.opengis.referencing.operation.MathTransform; import org.apache.sis.parameter.Parameters; import org.apache.sis.parameter.ParameterBuilder; @@ -270,7 +270,7 @@ public final class Equirectangular extends AbstractProvider { * @see MapProjection#MapProjection(Class, ParameterDescriptorGroup) */ public Equirectangular() { - super(Projection.class, PARAMETERS, + super(Conversion.class, PARAMETERS, EllipsoidalCS.class, true, CartesianCS.class, false, (byte) 2); diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/MapProjection.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/MapProjection.java index 09ef70ddd3..114fe3e8a4 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/MapProjection.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/MapProjection.java @@ -33,7 +33,7 @@ import org.opengis.parameter.ParameterNotFoundException; import org.opengis.referencing.cs.CartesianCS; import org.opengis.referencing.cs.EllipsoidalCS; import org.opengis.referencing.operation.MathTransform; -import org.opengis.referencing.operation.Projection; +import org.opengis.referencing.operation.Conversion; import static org.opengis.metadata.Identifier.AUTHORITY_KEY; import org.apache.sis.referencing.NamedIdentifier; import org.apache.sis.referencing.IdentifiedObjects; @@ -173,7 +173,7 @@ public abstract class MapProjection extends AbstractProvider { * @param parameters the set of parameters (never {@code null}). */ protected MapProjection(final ParameterDescriptorGroup parameters) { - super(Projection.class, parameters, + super(Conversion.class, parameters, EllipsoidalCS.class, true, CartesianCS.class, false, (byte) 2); diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/PseudoPlateCarree.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/PseudoPlateCarree.java index 5047e8c6d8..c53bf30ebb 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/PseudoPlateCarree.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/PseudoPlateCarree.java @@ -30,9 +30,6 @@ import org.apache.sis.metadata.iso.citation.Citations; * The semi-major and semi-minor axis lengths are ignored (they could be fixed to 1) but nevertheless declared * for allowing netCDF file encoding to declare the ellipsoid in pseudo-projection parameters. * - * <p>We do not declare that operation method as a {@link org.opengis.referencing.operation.Projection} because - * axis units are degrees.</p> - * * @author Martin Desruisseaux (Geomatys) * * @see Equirectangular diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/ZonedTransverseMercator.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/ZonedTransverseMercator.java index 52d12e96d2..0c63480b63 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/ZonedTransverseMercator.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/ZonedTransverseMercator.java @@ -22,7 +22,7 @@ import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.parameter.ParameterNotFoundException; import org.opengis.referencing.cs.EllipsoidalCS; -import org.opengis.referencing.operation.Projection; +import org.opengis.referencing.operation.Conversion; import org.opengis.referencing.operation.MathTransform; import org.apache.sis.measure.Units; import org.apache.sis.measure.Longitude; @@ -104,7 +104,7 @@ public final class ZonedTransverseMercator extends AbstractProvider { * because of the discontinuities between zones. */ public ZonedTransverseMercator() { - super(Projection.class, PARAMETERS, + super(Conversion.class, PARAMETERS, EllipsoidalCS.class, true, EllipsoidalCS.class, false, (byte) 2); diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java index 83750adbdd..25949cec40 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java @@ -394,12 +394,12 @@ public class DefaultMathTransformFactory extends AbstractFactory implements Math * {@code OperationMethod}s. The argument is usually (but not restricted to) one of the following types: * * <ul> + * <li>{@link org.opengis.referencing.operation.Conversion} + * for coordinate operations described by definitions (including map projections).</li> * <li>{@link org.opengis.referencing.operation.Transformation} * for coordinate operations described by empirically derived parameters.</li> - * <li>{@link org.opengis.referencing.operation.Conversion} - * for coordinate operations described by definitions.</li> - * <li>{@link org.opengis.referencing.operation.Projection} - * for conversions from geodetic latitudes and longitudes to plane (map) coordinates.</li> + * <li>{@link org.opengis.referencing.operation.PointMotionOperation} + * for changes due to the motion of the point between two coordinate epochs.</li> * <li>{@link SingleOperation} for all coordinate operations.</li> * </ul> * @@ -407,8 +407,8 @@ public class DefaultMathTransformFactory extends AbstractFactory implements Math * if this {@code MathTransformFactory} does not support filtering by the given type. * * @param type <code>{@linkplain SingleOperation}.class</code> for fetching all operation methods, - * <code>{@linkplain org.opengis.referencing.operation.Projection}.class</code> for - * fetching only map projection methods, <i>etc</i>. + * <code>{@linkplain org.opengis.referencing.operation.Transformation}.class</code> + * for operations defined by empirical parameters, <i>etc</i>. * @return methods available in this factory for coordinate operations of the given type. * * @see #getDefaultParameters(String) diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java index a71ea42375..cff1f41696 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/sql/EPSGFactoryTest.java @@ -43,7 +43,6 @@ import org.opengis.referencing.operation.CoordinateOperation; import org.opengis.referencing.operation.SingleOperation; import org.opengis.referencing.operation.OperationMethod; import org.opengis.referencing.operation.MathTransform; -import org.opengis.referencing.operation.Projection; import org.opengis.util.FactoryException; import org.apache.sis.system.Loggers; import org.apache.sis.referencing.CRS; @@ -651,34 +650,26 @@ public final class EPSGFactoryTest extends TestCaseWithLogs { final Set<String> parameters = factory.getAuthorityCodes(ParameterDescriptor.class); final Set<String> operations = factory.getAuthorityCodes(SingleOperation .class); final Set<String> conversions = factory.getAuthorityCodes(Conversion .class); - final Set<String> projections = factory.getAuthorityCodes(Projection .class); final Set<String> transformations = factory.getAuthorityCodes(Transformation .class); assertFalse(methods .isEmpty(), "Methods not found."); assertFalse(parameters .isEmpty(), "Parameters not found."); assertFalse(operations .isEmpty(), "Operations not found."); assertFalse(conversions .isEmpty(), "Conversions not found."); - assertFalse(projections .isEmpty(), "Projections not found."); assertFalse(transformations.isEmpty(), "Transformations not found."); assertTrue (methods .contains("9804"), "Shall contain “Mercator 1SP”"); assertTrue (parameters .contains("8805"), "Shall contain “Scale factor”"); assertTrue (operations .contains("1133"), "Shall contain “ED50 to WGS 84 (1)”"); assertFalse(conversions .contains("1133"), "Shall not contain “ED50 to WGS 84 (1)”"); - assertFalse(projections .contains("1133"), "Shall not contain “ED50 to WGS 84 (1)”"); assertTrue (transformations.contains("1133"), "Shall contain “ED50 to WGS 84 (1)”"); assertTrue (operations .contains("16001"), "Shall contain “UTM zone 1N”"); assertTrue (conversions .contains("16001"), "Shall contain “UTM zone 1N”"); - assertTrue (projections .contains("16001"), "Shall contain “UTM zone 1N”"); assertFalse(transformations.contains("16001"), "Shall not contain “UTM zone 1N”"); if (RUN_EXTENSIVE_TESTS) { assertTrue (conversions .size() < operations .size(), "Conversions shall be a subset of operations."); - assertTrue (projections .size() < operations .size(), "Projections shall be a subset of operations."); - assertTrue (projections .size() < conversions.size(), "Projections shall be a subset of conversions."); assertTrue (transformations.size() < operations .size(), "Transformations shall be a subset of operations."); - assertFalse(projections.containsAll(conversions), "Projections shall be a subset of conversions."); - assertTrue (conversions.containsAll(projections), "Projections shall be a subset of conversions."); assertTrue (operations .containsAll(conversions), "Conversion shall be a subset of operations."); assertTrue (operations .containsAll(transformations), "Transformations shall be a subset of operations."); assertTrue (Collections.disjoint(conversions, transformations), "Conversions cannot be transformations."); @@ -752,7 +743,7 @@ public final class EPSGFactoryTest extends TestCaseWithLogs { assertNotSame(projection, operation, "The defining conversion and the actual conversion should differ " + "because the actual conversion should have semi-axis length values."); - assertInstanceOf(Projection.class, projection); + assertInstanceOf(Conversion.class, projection); assertNotNull(projection.getSourceCRS()); assertNotNull(projection.getTargetCRS()); assertNotNull(projection.getMathTransform()); diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java index 45672693bb..dd933a7b54 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/CoordinateOperationFinderTest.java @@ -33,7 +33,6 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.CoordinateOperation; import org.opengis.referencing.operation.SingleOperation; import org.opengis.referencing.operation.Conversion; -import org.opengis.referencing.operation.Projection; import org.opengis.referencing.operation.Transformation; import org.opengis.referencing.operation.TransformException; import org.opengis.referencing.operation.ConcatenatedOperation; @@ -454,7 +453,7 @@ public final class CoordinateOperationFinderTest extends MathTransformTestCase { assertSame(sourceCRS, operation.getSourceCRS()); assertSame(targetCRS, operation.getTargetCRS()); assertEquals("TM", operation.getName().getCode()); - assertInstanceOf(Projection.class, operation); + assertInstanceOf(Conversion.class, operation); final ParameterValueGroup param = ((SingleOperation) operation).getParameterValues(); assertEquals(6370997, param.parameter("semi_major" ).doubleValue()); 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 6c3e80b065..2f693ca840 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 @@ -239,13 +239,12 @@ public final class DefaultConversionTest extends TestCase { * Now create a normal conversion from the defining one, * but add a swapping of (latitude, longitude) axes. */ - final DefaultConversion completed = definingConversion.specialize( - DefaultConversion.class, // In normal use, this would be 'Conversion.class'. + final Conversion completed = definingConversion.specialize( changeCS(reference.getSourceCRS(), HardCodedCS.GEODETIC_φλ), reference.getTargetCRS(), null); - verifyProperties(completed, true); + verifyProperties(assertInstanceOf(DefaultConversion.class, completed), true); } /** @@ -270,17 +269,16 @@ public final class DefaultConversionTest extends TestCase { * When asking for a "specialization" with the same properties, * we should get the existing instance since no change is needed. */ - assertSame(op, op.specialize(Conversion.class, op.getSourceCRS(), op.getTargetCRS(), null)); + assertSame(op, op.specialize(op.getSourceCRS(), op.getTargetCRS(), null)); /* * Reducing the number of dimensions to 2 and swapping (latitude, longitude) axes. */ - op = op.specialize(DefaultConversion.class, op.getSourceCRS(), - changeCS(op.getTargetCRS(), HardCodedCS.GEODETIC_φλ), null); + Conversion c = op.specialize(op.getSourceCRS(), changeCS(op.getTargetCRS(), HardCodedCS.GEODETIC_φλ), null); assertMatrixEquals(Matrices.create(3, 4, new double[] { 0, 1, 0, 0, 1, 0, 0, OFFSET, 0, 0, 0, 1 - }), MathTransforms.getMatrix(op.getMathTransform()), STRICT, + }), MathTransforms.getMatrix(c.getMathTransform()), STRICT, "Longitude rotation of a two-dimensional CRS"); } @@ -310,8 +308,7 @@ public final class DefaultConversionTest extends TestCase { MathTransforms.getMatrix(op.getMathTransform()), STRICT, "Longitude rotation of a time-varying CRS"); - op = op.specialize( - DefaultConversion.class, // In normal use, this would be 'Conversion.class'. + Conversion c = op.specialize( op.getSourceCRS(), // Keep the same source CRS. changeCS(op.getTargetCRS(), HardCodedCS.GEODETIC_φλ), // Swap axis order. null); @@ -320,7 +317,7 @@ public final class DefaultConversionTest extends TestCase { 0, 0, 1, 0, 0, 1, 0, OFFSET, 0, 0, 0, 1), - MathTransforms.getMatrix(op.getMathTransform()), STRICT, + MathTransforms.getMatrix(c.getMathTransform()), STRICT, "Longitude rotation of a time-varying CRS"); } @@ -334,12 +331,12 @@ public final class DefaultConversionTest extends TestCase { final DefaultConversion op = createLongitudeRotation(true); IllegalArgumentException e; e = assertThrows(IllegalArgumentException.class, - () -> op.specialize(Conversion.class, HardCodedCRS.WGS84, HardCodedCRS.NTF_NORMALIZED_AXES, null), + () -> op.specialize(HardCodedCRS.WGS84, HardCodedCRS.NTF_NORMALIZED_AXES, null), "Should not have accepted to change the geodetic datum."); assertMessageContains(e, "sourceCRS", "Nouvelle Triangulation Française"); e = assertThrows(IllegalArgumentException.class, - () -> op.specialize(Conversion.class, HardCodedCRS.NTF_NORMALIZED_AXES, HardCodedCRS.WGS84, null), + () -> op.specialize(HardCodedCRS.NTF_NORMALIZED_AXES, HardCodedCRS.WGS84, null), "Should not have accepted to change the geodetic datum."); assertMessageContains(e, "targetCRS", "Nouvelle Triangulation Française"); } diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java index 37aaf8704a..fea87bd442 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/operation/provider/ProvidersTest.java @@ -22,7 +22,6 @@ import java.util.IdentityHashMap; import org.opengis.util.GenericName; import org.opengis.metadata.Identifier; import org.opengis.referencing.cs.VerticalCS; -import org.opengis.referencing.operation.Projection; import org.opengis.parameter.GeneralParameterDescriptor; import org.opengis.parameter.ParameterDescriptorGroup; @@ -207,7 +206,7 @@ public final class ProvidersTest extends TestCase { final int expected; if (method.sourceCSType == VerticalCS.class) { expected = 1; - } else if (Projection.class.isAssignableFrom(method.getOperationType())) { + } else if (method instanceof MapProjection) { expected = 2; } else if (name.endsWith("1D")) { expected = 1; 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 622899fa08..5a29446915 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 @@ -24,20 +24,20 @@ import java.util.ServiceLoader; import org.opengis.util.FactoryException; import org.opengis.util.NoSuchIdentifierException; import org.opengis.referencing.operation.Conversion; -import org.opengis.referencing.operation.Projection; import org.opengis.referencing.operation.SingleOperation; import org.opengis.referencing.operation.OperationMethod; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.parameter.ParameterValueGroup; import org.apache.sis.parameter.Parameterized; +import org.apache.sis.referencing.crs.DefaultProjectedCRS; import org.apache.sis.referencing.operation.DefaultConversion; import org.apache.sis.referencing.operation.matrix.Matrix2; import org.apache.sis.referencing.operation.matrix.Matrices; -import org.apache.sis.referencing.crs.DefaultProjectedCRS; -import org.apache.sis.referencing.factory.InvalidGeodeticParameterException; import org.apache.sis.referencing.operation.provider.Affine; import org.apache.sis.referencing.operation.provider.Mercator1SP; +import org.apache.sis.referencing.operation.provider.MapProjection; +import org.apache.sis.referencing.factory.InvalidGeodeticParameterException; import org.apache.sis.util.privy.Constants; import org.apache.sis.measure.Units; @@ -149,19 +149,16 @@ public final class DefaultMathTransformFactoryTest extends TestCase { final DefaultMathTransformFactory factory = factory(); final Set<OperationMethod> transforms = factory.getAvailableMethods(SingleOperation.class); final Set<OperationMethod> conversions = factory.getAvailableMethods(Conversion.class); - final Set<OperationMethod> projections = factory.getAvailableMethods(Projection.class); /* * Following tests should not cause loading of more classes than needed. */ assertFalse(transforms .isEmpty()); assertFalse(conversions.isEmpty()); - assertFalse(projections.isEmpty()); assertTrue (transforms.contains(factory.getOperationMethod(Constants.AFFINE))); /* * Following tests will force instantiation of all remaining OperationMethod. */ - assertTrue(transforms .containsAll(conversions), "Conversions should be a subset of transforms."); - assertTrue(conversions.containsAll(projections), "Projections should be a subset of conversions."); + assertTrue(transforms.containsAll(conversions), "Conversions should be a subset of transforms."); } /** @@ -213,8 +210,12 @@ public final class DefaultMathTransformFactoryTest extends TestCase { */ final MathTransformFactory factory = factory(); final Map<String,?> dummyName = Map.of(DefaultProjectedCRS.NAME_KEY, "Test"); - final Collection<OperationMethod> methods = factory.getAvailableMethods(Projection.class); + final Collection<OperationMethod> methods = factory.getAvailableMethods(Conversion.class); + int count = 0; for (final OperationMethod method : methods) { + if (!(method instanceof MapProjection)) { + continue; + } final String classification = method.getName().getCode(); ParameterValueGroup pg = factory.getDefaultParameters(classification); pg.parameter("semi_major").setValue(6377563.396); @@ -295,7 +296,9 @@ public final class DefaultMathTransformFactoryTest extends TestCase { final Conversion projection = crs.getConversionFromBase(); assertSame(mt, projection.getMathTransform(), classification); assertEquals(projection.getMethod().getName().getCode(), classification); + count++; } + assertTrue(count >= 15, "Map projection methods not found."); } /** diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java index d649065fe5..74817c606a 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java @@ -100,7 +100,7 @@ public class CoordinateOperationMethods extends HTMLGenerator { /** * Values returned by {@link #category(OperationMethod)}. */ - private static final int PROJECTION = 1, CONVERSION = 2, TRANSFORMATION = 3; + private static final int CONVERSION = 1, TRANSFORMATION = 3; /** * Parameters to default to the latitude of origin. We can hardly detect those cases @@ -192,7 +192,6 @@ public class CoordinateOperationMethods extends HTMLGenerator { closeTags(innerUL); reopenTag("li"); switch (nc) { - case PROJECTION: println("Projections"); break; case CONVERSION: println("Conversions"); break; case TRANSFORMATION: println("Tranformations"); break; default: throw new AssertionError(category); @@ -522,7 +521,6 @@ public class CoordinateOperationMethods extends HTMLGenerator { */ private static int category(final OperationMethod method) { final Class<?> c = getOperationType((DefaultOperationMethod) method); - if (Projection .class.isAssignableFrom(c)) return PROJECTION; if (Conversion .class.isAssignableFrom(c)) return CONVERSION; if (Transformation.class.isAssignableFrom(c)) return TRANSFORMATION; return 0; diff --git a/geoapi/snapshot b/geoapi/snapshot index b822d026bd..1dd682e1b3 160000 --- a/geoapi/snapshot +++ b/geoapi/snapshot @@ -1 +1 @@ -Subproject commit b822d026bd9e4692a159a9bd2bc158716da2e721 +Subproject commit 1dd682e1b3950d7024b17f6639673b896f701441