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

commit 550ee271a91e817b81f928ad5f6ede55496cbc57
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Wed Jul 24 16:05:54 2024 +0200

    Implement geodetic and vertical DynamicReferenceFrame classes.
    The coordinate operation factory has not yet been updated for taking the 
frame reference epoch in account.
---
 .../sis/referencing/datum/AbstractDatum.java       |  58 +++++++-
 .../sis/referencing/datum/BursaWolfParameters.java |  20 +--
 .../referencing/datum/DefaultGeodeticDatum.java    | 152 ++++++++++++++++-----
 .../referencing/datum/DefaultVerticalDatum.java    |  83 +++++++++++
 .../referencing/factory/GeodeticObjectFactory.java |  71 +++++++++-
 geoapi/snapshot                                    |   2 +-
 6 files changed, 334 insertions(+), 52 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java
index 56c14ae7c9..ed1d8e6b57 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java
@@ -47,6 +47,7 @@ import static org.apache.sis.util.Utilities.deepEquals;
 import static org.apache.sis.util.collection.Containers.property;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
+import org.opengis.referencing.datum.DynamicReferenceFrame;
 import org.opengis.metadata.Identifier;
 
 // Specific to the geoapi-4.0 branch:
@@ -311,6 +312,17 @@ public class AbstractDatum extends 
AbstractIdentifiedObject implements Datum {
         return Optional.ofNullable(anchorEpoch);
     }
 
+    /**
+     * Returns the frame reference epoch if this datum is dynamic, or {@code 
null} if this datum is static.
+     * This method is overridden with public access in Apache SIS {@code 
Dynamic} subclasses.
+     * The default implementation should be suitable for non-SIS 
implementations.
+     *
+     * @return the reference epoch if this datum is dynamic, or {@code null} 
if this datum is static.
+     */
+    Temporal getFrameReferenceEpoch() {
+        return (this instanceof DynamicReferenceFrame) ? 
((DynamicReferenceFrame) this).getFrameReferenceEpoch() : null;
+    }
+
     /**
      * Returns the date on which the datum definition was published.
      *
@@ -395,6 +407,12 @@ public class AbstractDatum extends 
AbstractIdentifiedObject implements Datum {
      * {@linkplain #getAnchorDefinition() anchor definition}, {@linkplain 
#getAnchorEpoch() anchor epoch},
      * and the {@linkplain #getDomains() domains}.
      *
+     * <h4>Static versus dynamic datum</h4>
+     * If this datum implements the {@link DynamicReferenceFrame} interface, 
then the given object needs
+     * to also implement that interfaces and provide the same reference epoch 
for being considered equal.
+     * Conversely, if this datum does not implement {@link 
DynamicReferenceFrame}, then the given object
+     * also need to <em>not</em> implement that interface for being considered 
equal.
+     *
      * @param  object  the object to compare to {@code this}.
      * @param  mode    {@link ComparisonMode#STRICT STRICT} for performing a 
strict comparison, or
      *                 {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} 
for comparing only
@@ -409,13 +427,18 @@ public class AbstractDatum extends 
AbstractIdentifiedObject implements Datum {
         switch (mode) {
             case STRICT: {
                 final var that = (AbstractDatum) object;
-                return Objects.equals(anchorEpoch,      that.anchorEpoch) &&
-                       Objects.equals(anchorDefinition, that.anchorDefinition);
+                return Objects.equals(anchorEpoch,       that.anchorEpoch) &&
+                       Objects.equals(anchorDefinition,  
that.anchorDefinition) &&
+                       Objects.equals(publicationDate,   that.publicationDate) 
&&
+                       Objects.equals(conventionalRS,    that.conventionalRS);
             }
             case BY_CONTRACT: {
-                final Datum that = (Datum) object;
-                return deepEquals(getAnchorEpoch(),      
that.getAnchorEpoch(), mode) &&
-                       deepEquals(getAnchorDefinition(), 
that.getAnchorDefinition(), mode);
+                final var that = (Datum) object;
+                return compareDynamicReferenceFrames(that, mode) &&
+                       deepEquals(getAnchorEpoch(),      
that.getAnchorEpoch(), mode) &&
+                       deepEquals(getAnchorDefinition(), 
that.getAnchorDefinition(), mode) &&
+                       deepEquals(getPublicationDate(),  
that.getPublicationDate(), mode) &&
+                       deepEquals(getConventionalRS(),   
that.getConventionalRS(), mode);
             }
             default: {
                 /*
@@ -428,6 +451,9 @@ public class AbstractDatum extends AbstractIdentifiedObject 
implements Datum {
                  * and parameters. We extend this rule to datum as well.
                  */
                 final var that = (Datum) object;
+                if (!compareDynamicReferenceFrames(that, mode)) {
+                    return false;
+                }
                 final Boolean match = 
Identifiers.hasCommonIdentifier(getIdentifiers(), that.getIdentifiers());
                 if (match != null) {
                     return match;
@@ -438,17 +464,35 @@ public class AbstractDatum extends 
AbstractIdentifiedObject implements Datum {
         }
     }
 
+    /**
+     * Checks whether this datum and the other datum are both static or both 
dynamic.
+     * In the latter case, checks also whether the two datum have the same 
reference epoch.
+     *
+     * @param  that  the other datum to compare with this datum.
+     * @param  mode  the comparison mode.
+     * @return whether the two reference frames are equal in their static 
versus dynamic aspect.
+     */
+    private boolean compareDynamicReferenceFrames(final Datum that, final 
ComparisonMode mode) {
+        final Temporal frameReferenceEpoch = getFrameReferenceEpoch();
+        if (frameReferenceEpoch != null) {
+            return (that instanceof DynamicReferenceFrame) &&
+                    deepEquals(frameReferenceEpoch, ((DynamicReferenceFrame) 
that).getFrameReferenceEpoch(), mode);
+        } else {
+            return !(that instanceof DynamicReferenceFrame);
+        }
+    }
+
     /**
      * Invoked by {@code hashCode()} for computing the hash code when first 
needed.
      * See {@link 
org.apache.sis.referencing.AbstractIdentifiedObject#computeHashCode()}
      * for more information.
      *
      * @return the hash code value. This value may change in any future Apache 
SIS version.
-     * @hidden because not useful.
+     * @hidden because nothing new to said.
      */
     @Override
     protected long computeHashCode() {
-        return super.computeHashCode() + Objects.hash(anchorDefinition, 
anchorEpoch);
+        return super.computeHashCode() + Objects.hash(anchorDefinition, 
anchorEpoch, publicationDate, conventionalRS);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/BursaWolfParameters.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/BursaWolfParameters.java
index 4a41244b4e..a38bd2c7d8 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/BursaWolfParameters.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/BursaWolfParameters.java
@@ -412,7 +412,7 @@ public class BursaWolfParameters extends FormattableObject 
implements Cloneable,
 
     /**
      * Returns the parameter at the given index. If this {@code 
BursaWolfParameters} is time-dependent,
-     * then the returned value shall be corrected the time elapsed since the 
reference time.
+     * then the returned value shall be corrected for the time elapsed since 
the reference time.
      *
      * The {@code factor} argument shall be the value computed by {@link 
#period(Temporal)},
      * multiplied by 1000 for all {@code index} values except 6.
@@ -498,17 +498,17 @@ public class BursaWolfParameters extends 
FormattableObject implements Cloneable,
         DoubleDouble RS = DoubleDouble.SECONDS_TO_RADIANS;
         DoubleDouble S = param(6, period).divide(PPM).add(1);       // S = 1 + 
dS / PPM;
         RS = RS.multiply(S);                                        // RS = 
toRadians(1″) * S;
-        final DoubleDouble rX = param(3, mp).multiply(RS);
-        final DoubleDouble rY = param(4, mp).multiply(RS);
-        final DoubleDouble rZ = param(5, mp).multiply(RS);
-        final DoubleDouble mX = rX.negate();
-        final DoubleDouble mY = rY.negate();
-        final DoubleDouble mZ = rZ.negate();
+        final DoubleDouble pX = param(3, mp).multiply(RS);
+        final DoubleDouble pY = param(4, mp).multiply(RS);
+        final DoubleDouble pZ = param(5, mp).multiply(RS);
+        final DoubleDouble mX = pX.negate();
+        final DoubleDouble mY = pY.negate();
+        final DoubleDouble mZ = pZ.negate();
         final Integer       O = 0;                                  // Fetch 
Integer instance only once.
         return Matrices.create(4, 4, new Number[] {
-                 S,  mZ,  rY,  param(0, mp),
-                rZ,   S,  mX,  param(1, mp),
-                mY,  rX,   S,  param(2, mp),
+                 S,  mZ,  pY,  param(0, mp),
+                pZ,   S,  mX,  param(1, mp),
+                mY,  pX,   S,  param(2, mp),
                  O,   O,   O,  1});
     }
 
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
index c2e72fb9b4..37d1e44f38 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
@@ -19,7 +19,6 @@ package org.apache.sis.referencing.datum;
 import java.util.Map;
 import java.util.Arrays;
 import java.util.Objects;
-import java.util.logging.Logger;
 import java.time.temporal.Temporal;
 import jakarta.xml.bind.annotation.XmlType;
 import jakarta.xml.bind.annotation.XmlElement;
@@ -46,7 +45,6 @@ import org.apache.sis.referencing.internal.AnnotatedMatrix;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.privy.CollectionsExt;
-import org.apache.sis.system.Loggers;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.io.wkt.Formatter;
 import static org.apache.sis.util.Utilities.deepEquals;
@@ -55,6 +53,7 @@ import static 
org.apache.sis.util.ArgumentChecks.ensureNonNullElement;
 import static org.apache.sis.referencing.privy.WKTUtilities.toFormattable;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
+import org.opengis.referencing.datum.DynamicReferenceFrame;
 import org.opengis.metadata.Identifier;
 
 
@@ -129,7 +128,7 @@ import org.opengis.metadata.Identifier;
  * constants.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.4
+ * @version 1.5
  *
  * @see DefaultEllipsoid
  * @see DefaultPrimeMeridian
@@ -144,13 +143,6 @@ import org.opengis.metadata.Identifier;
 })
 @XmlRootElement(name = "GeodeticDatum")
 public class DefaultGeodeticDatum extends AbstractDatum implements 
GeodeticDatum {
-    /**
-     * The logger for coordinate operations.
-     *
-     * @see #getPositionVectorTransformation(GeodeticDatum, Extent)
-     */
-    private static final Logger LOGGER = 
Logger.getLogger(Loggers.COORDINATE_OPERATION);
-
     /**
      * Serial number for inter-operability with different versions.
      */
@@ -306,8 +298,13 @@ public class DefaultGeodeticDatum extends AbstractDatum 
implements GeodeticDatum
      *         given object itself), or {@code null} if the argument was null.
      */
     public static DefaultGeodeticDatum castOrCopy(final GeodeticDatum object) {
-        return (object == null) || (object instanceof DefaultGeodeticDatum)
-                ? (DefaultGeodeticDatum) object : new 
DefaultGeodeticDatum(object);
+        if (object == null || object instanceof DefaultGeodeticDatum) {
+            return (DefaultGeodeticDatum) object;
+        }
+        if (object instanceof DynamicReferenceFrame) {
+            return new Dynamic(object);
+        }
+        return new DefaultGeodeticDatum(object);
     }
 
     /**
@@ -435,7 +432,8 @@ public class DefaultGeodeticDatum extends AbstractDatum 
implements GeodeticDatum
                  * is defined in such a way that matrix should always be 
invertible. If it happen anyway,
                  * returning `null` is allowed by this method's contract.
                  */
-                Logging.unexpectedException(LOGGER, 
DefaultGeodeticDatum.class, "getPositionVectorTransformation", e);
+                Logging.unexpectedException(CoordinateOperations.LOGGER,
+                        DefaultGeodeticDatum.class, 
"getPositionVectorTransformation", e);
             }
             /*
              * No direct tranformation found. Search for a path through an 
intermediate datum.
@@ -468,7 +466,8 @@ public class DefaultGeodeticDatum extends AbstractDatum 
implements GeodeticDatum
                                     Matrix m = 
MatrixSIS.castOrCopy(step2).inverse().multiply(step1);
                                     return AnnotatedMatrix.indirect(m, useAOI);
                                 } catch (NoninvertibleMatrixException e) {
-                                    Logging.unexpectedException(LOGGER, 
DefaultGeodeticDatum.class, "getPositionVectorTransformation", e);
+                                    
Logging.unexpectedException(CoordinateOperations.LOGGER,
+                                            DefaultGeodeticDatum.class, 
"getPositionVectorTransformation", e);
                                 }
                             }
                         }
@@ -481,21 +480,6 @@ public class DefaultGeodeticDatum extends AbstractDatum 
implements GeodeticDatum
         return null;
     }
 
-    /**
-     * Invokes {@link 
BursaWolfParameters#getPositionVectorTransformation(Temporal)} for a date 
calculated from
-     * the temporal elements on the given extent. This method chooses an 
instant located midway between the
-     * start and end time.
-     */
-    private static Matrix createTransformation(final BursaWolfParameters 
bursaWolf, final Extent areaOfInterest) {
-        /*
-         * Implementation note: we know that we do not need to compute an 
instant if the parameters is
-         * not a subclass of BursaWolfParameters. This optimisation covers the 
vast majority of cases.
-         */
-        return bursaWolf.getPositionVectorTransformation(bursaWolf.getClass() 
!= BursaWolfParameters.class ?
-                Extents.getInstant(areaOfInterest, null, 0.5).orElse(null) : 
null);
-                // 0.5 is for choosing the instant midway between start and 
end.
-    }
-
     /**
      * Returns the best parameters matching the given criteria, or {@code 
null} if none.
      */
@@ -511,6 +495,112 @@ public class DefaultGeodeticDatum extends AbstractDatum 
implements GeodeticDatum
         return selector.best();
     }
 
+    /**
+     * Returns the position vector transformation (geocentric domain) as an 
affine transform.
+     * If this datum is dynamic, the frame reference epoch is used.
+     * Otherwise, a time is computed from the temporal area of interest.
+     *
+     * @see DynamicReferenceFrame#getFrameReferenceEpoch()
+     * @see BursaWolfParameters#getPositionVectorTransformation(Temporal)
+     */
+    private Matrix createTransformation(final BursaWolfParameters bursaWolf, 
final Extent areaOfInterest) {
+        Temporal epoch = null;
+        /*
+         * Implementation note: we know that we do not need to compute an 
instant if the parameters is
+         * not a subclass of BursaWolfParameters. This optimisation covers the 
vast majority of cases.
+         */
+        if (bursaWolf.getClass() != BursaWolfParameters.class) {
+            epoch = getFrameReferenceEpoch();
+            if (epoch == null) {
+                epoch = Extents.getInstant(areaOfInterest, null, 
0.5).orElse(null);
+                // 0.5 is for choosing the instant midway between start and 
end.
+            }
+        }
+        return bursaWolf.getPositionVectorTransformation(epoch);
+    }
+
+    /**
+     * A geodetic reference frame in which some of the defining parameters 
have time dependency.
+     * The parameter values are valid at the time given by the
+     * {@linkplain #getFrameReferenceEpoch() frame reference epoch}.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @version 1.5
+     * @since   1.5
+     */
+    public static class Dynamic extends DefaultGeodeticDatum implements 
DynamicReferenceFrame {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = 6117199873814779662L;
+
+        /**
+         * The epoch to which the definition of the dynamic reference frame is 
referenced.
+         */
+        @SuppressWarnings("serial")                     // Standard Java 
implementations are serializable.
+        private final Temporal frameReferenceEpoch;
+
+        /**
+         * Creates a dynamic reference frame from the given properties.
+         * See super-class constructor for more information.
+         *
+         * @param  properties     the properties to be given to the identified 
object.
+         * @param  ellipsoid      the ellipsoid.
+         * @param  primeMeridian  the prime meridian.
+         * @param  epoch          the epoch to which the definition of the 
dynamic reference frame is referenced.
+         */
+        public Dynamic(Map<String,?> properties, Ellipsoid ellipsoid, 
PrimeMeridian primeMeridian, Temporal epoch) {
+            super(properties, ellipsoid, primeMeridian);
+            frameReferenceEpoch = Objects.requireNonNull(epoch);
+        }
+
+        /**
+         * Creates a new datum with the same values as the specified datum, 
which must be dynamic.
+         *
+         * @param  datum  the datum to copy.
+         * @throws ClassCastException if the given datum is not an instance of 
{@link DynamicReferenceFrame}.
+         *
+         * @see #castOrCopy(GeodeticDatum)
+         */
+        protected Dynamic(final GeodeticDatum datum) {
+            super(datum);
+            frameReferenceEpoch = 
Objects.requireNonNull(((DynamicReferenceFrame) 
datum).getFrameReferenceEpoch());
+        }
+
+        /**
+         * Returns the epoch to which the coordinates of stations defining the 
dynamic reference frame are referenced.
+         * The type of the returned object depends on the epoch accuracy and 
the calendar in use.
+         * It may be merely a {@link java.time.Year}.
+         *
+         * @return the epoch to which the definition of the dynamic reference 
frame is referenced.
+         */
+        @Override
+        public Temporal getFrameReferenceEpoch() {
+            return frameReferenceEpoch;
+        }
+
+        /**
+         * Compares the specified object with this datum for equality.
+         *
+         * @hidden because nothing new to said.
+         */
+        @Override
+        public boolean equals(final Object object, final ComparisonMode mode) {
+            return super.equals(object) && (mode != ComparisonMode.STRICT ||
+                    frameReferenceEpoch.equals(((Dynamic) 
object).frameReferenceEpoch));
+        }
+
+        /**
+         * Invoked by {@code hashCode()} for computing the hash code when 
first needed.
+         *
+         * @hidden because nothing new to said.
+         */
+        @Override
+        protected long computeHashCode() {
+            return super.computeHashCode() + 31 * 
frameReferenceEpoch.hashCode();
+        }
+    }
+
     /**
      * Returns {@code true} if either the {@linkplain #getName() primary name} 
or at least
      * one {@linkplain #getAlias() alias} matches the given string according 
heuristic rules.
@@ -580,13 +670,13 @@ public class DefaultGeodeticDatum extends AbstractDatum 
implements GeodeticDatum
         }
         switch (mode) {
             case STRICT: {
-                final DefaultGeodeticDatum that = (DefaultGeodeticDatum) 
object;
+                final var that = (DefaultGeodeticDatum) object;
                 return Objects.equals(this.ellipsoid,     that.ellipsoid)     
&&
                        Objects.equals(this.primeMeridian, that.primeMeridian) 
&&
                         Arrays.equals(this.bursaWolf,     that.bursaWolf);
             }
             default: {
-                final GeodeticDatum that = (GeodeticDatum) object;
+                final var that = (GeodeticDatum) object;
                 return deepEquals(getEllipsoid(),     that.getEllipsoid(),     
mode) &&
                        deepEquals(getPrimeMeridian(), that.getPrimeMeridian(), 
mode);
                 /*
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
index 6bbcdd48d0..b1fef4a79f 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
@@ -18,6 +18,7 @@ package org.apache.sis.referencing.datum;
 
 import java.util.Map;
 import java.util.Objects;
+import java.time.temporal.Temporal;
 import jakarta.xml.bind.annotation.XmlType;
 import jakarta.xml.bind.annotation.XmlElement;
 import jakarta.xml.bind.annotation.XmlRootElement;
@@ -34,6 +35,7 @@ import org.apache.sis.metadata.privy.ImplementationHelper;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
 import java.util.Optional;
+import org.opengis.referencing.datum.DynamicReferenceFrame;
 import org.opengis.referencing.datum.RealizationMethod;
 import org.opengis.metadata.Identifier;
 
@@ -211,6 +213,87 @@ public class DefaultVerticalDatum extends AbstractDatum 
implements VerticalDatum
         return Optional.ofNullable(method);
     }
 
+    /**
+     * A vertical reference frame in which some of the defining parameters 
have time dependency.
+     * The parameter values are valid at the time given by the
+     * {@linkplain #getFrameReferenceEpoch() frame reference epoch}.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @version 1.5
+     * @since   1.5
+     */
+    public static class Dynamic extends DefaultVerticalDatum implements 
DynamicReferenceFrame {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = -2047994195060747008L;
+
+        /**
+         * The epoch to which the definition of the dynamic reference frame is 
referenced.
+         */
+        @SuppressWarnings("serial")                     // Standard Java 
implementations are serializable.
+        private final Temporal frameReferenceEpoch;
+
+        /**
+         * Creates a dynamic reference frame from the given properties.
+         * See super-class constructor for more information.
+         *
+         * @param  properties     the properties to be given to the identified 
object.
+         * @param  epoch          the epoch to which the definition of the 
dynamic reference frame is referenced.
+         */
+        public Dynamic(Map<String,?> properties, RealizationMethod method, 
Temporal epoch) {
+            super(properties, method);
+            frameReferenceEpoch = Objects.requireNonNull(epoch);
+        }
+
+        /**
+         * Creates a new datum with the same values as the specified datum, 
which must be dynamic.
+         *
+         * @param  datum   the datum to copy.
+         * @param  method  the realization method (geoid, tidal, <i>etc.</i>), 
or {@code null} if unspecified.
+         * @throws ClassCastException if the given datum is not an instance of 
{@link DynamicReferenceFrame}.
+         *
+         * @see #castOrCopy(VerticalDatum)
+         */
+        protected Dynamic(final VerticalDatum datum) {
+            super(datum);
+            frameReferenceEpoch = 
Objects.requireNonNull(((DynamicReferenceFrame) 
datum).getFrameReferenceEpoch());
+        }
+
+        /**
+         * Returns the epoch to which the coordinates of stations defining the 
dynamic reference frame are referenced.
+         * The type of the returned object depends on the epoch accuracy and 
the calendar in use.
+         * It may be merely a {@link java.time.Year}.
+         *
+         * @return the epoch to which the definition of the dynamic reference 
frame is referenced.
+         */
+        @Override
+        public Temporal getFrameReferenceEpoch() {
+            return frameReferenceEpoch;
+        }
+
+        /**
+         * Compares the specified object with this datum for equality.
+         *
+         * @hidden because nothing new to said.
+         */
+        @Override
+        public boolean equals(final Object object, final ComparisonMode mode) {
+            return super.equals(object) && (mode != ComparisonMode.STRICT ||
+                    frameReferenceEpoch.equals(((Dynamic) 
object).frameReferenceEpoch));
+        }
+
+        /**
+         * Invoked by {@code hashCode()} for computing the hash code when 
first needed.
+         *
+         * @hidden because nothing new to said.
+         */
+        @Override
+        protected long computeHashCode() {
+            return super.computeHashCode() + 31 * 
frameReferenceEpoch.hashCode();
+        }
+    }
+
     /**
      * Compares this vertical datum with the specified object for equality.
      *
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 51d03bcb17..623e89a925 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
@@ -65,6 +65,7 @@ import org.apache.sis.xml.XML;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
 import java.time.temporal.Temporal;
+import org.opengis.referencing.datum.DynamicReferenceFrame;
 
 
 /**
@@ -611,9 +612,9 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
     }
 
     /**
-     * Creates a geodetic reference frame from ellipsoid and (optionally) 
Bursa-Wolf parameters.
+     * Creates a static geodetic reference frame from ellipsoid and 
(optionally) Bursa-Wolf parameters.
      * Geodetic reference frame defines the location and orientation of an 
ellipsoid that approximates the shape of the earth.
-     * This datum can be used with geographic, geocentric and engineering CRS.
+     * This datum can be used with geographic and geocentric <abbr>CRS</abbr>.
      *
      * <h4>Dependencies</h4>
      * The components needed by this method can be created by the following 
methods:
@@ -650,6 +651,39 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
         return unique("createGeodeticDatum", datum);
     }
 
+    /**
+     * Creates a dynamic geodetic reference frame from ellipsoid and frame 
reference epoch.
+     * The arguments are the same as for the {@linkplain 
#createGeodeticDatum(Map, Ellipsoid,
+     * PrimeMeridian) static datum}, with the addition of a mandatory frame 
reference epoch.
+     * The returned object implements the {@link DynamicReferenceFrame} 
interface.
+     *
+     * @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.
+     * @param  epoch          the epoch to which the definition of the dynamic 
reference frame is referenced.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see DefaultGeodeticDatum.Dynamic#Dynamic(Map, Ellipsoid, 
PrimeMeridian, Temporal)
+     * @see GeodeticAuthorityFactory#createGeodeticDatum(String)
+     *
+     * @since 1.5
+     */
+    @Override
+    public GeodeticDatum createGeodeticDatum(final Map<String,?> properties,
+                                             final Ellipsoid     ellipsoid,
+                                             final PrimeMeridian primeMeridian,
+                                             final Temporal      epoch)
+            throws FactoryException
+    {
+        final DefaultGeodeticDatum datum;
+        try {
+            datum = new DefaultGeodeticDatum.Dynamic(complete(properties), 
ellipsoid, primeMeridian, epoch);
+        } catch (IllegalArgumentException exception) {
+            throw new InvalidGeodeticParameterException(exception);
+        }
+        return unique("createGeodeticDatum", datum);
+    }
+
     /**
      * Creates a prime meridian, relative to Greenwich.
      * Defines the origin from which longitude values are determined.
@@ -998,7 +1032,7 @@ public class GeodeticObjectFactory extends AbstractFactory 
implements CRSFactory
     }
 
     /**
-     * Creates a vertical datum from a realization method.
+     * Creates a static vertical datum from a realization method.
      * The default implementation creates a {@link DefaultVerticalDatum} 
instance.
      *
      * @param  properties  name and other properties to give to the new object.
@@ -1024,6 +1058,37 @@ public class GeodeticObjectFactory extends 
AbstractFactory implements CRSFactory
         return unique("createVerticalDatum", datum);
     }
 
+    /**
+     * Creates a dynamic vertical datum from a realization method and a frame 
reference epoch.
+     * The arguments are the same as for the {@linkplain 
#createVerticalDatum(Map, RealizationMethod)
+     * static datum}, with the addition of a mandatory frame reference epoch.
+     * The returned object implements the {@link DynamicReferenceFrame} 
interface.
+     *
+     * @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.
+     * @throws FactoryException if the object creation failed.
+     *
+     * @see DefaultVerticalDatum.Dynamic#Dynamic(Map, RealizationMethod, 
Temporal)
+     * @see GeodeticAuthorityFactory#createVerticalDatum(String)
+     *
+     * @since 2.0 (temporary version number until this branch is released)
+     */
+    @Override
+    public VerticalDatum createVerticalDatum(final Map<String,?> properties,
+                                             final RealizationMethod method,
+                                             final Temporal epoch)
+            throws FactoryException
+    {
+        final DefaultVerticalDatum datum;
+        try {
+            datum = new DefaultVerticalDatum.Dynamic(complete(properties), 
method, epoch);
+        } catch (IllegalArgumentException exception) {
+            throw new InvalidGeodeticParameterException(exception);
+        }
+        return unique("createVerticalDatum", datum);
+    }
+
     /**
      * Creates a vertical coordinate system.
      * This coordinate system can be used with vertical and derived CRS.
diff --git a/geoapi/snapshot b/geoapi/snapshot
index 8281380030..367366e67f 160000
--- a/geoapi/snapshot
+++ b/geoapi/snapshot
@@ -1 +1 @@
-Subproject commit 828138003047cb68d25fb162732435be54ef2891
+Subproject commit 367366e67f7f860e100e0e8dafa3a03f34162e71

Reply via email to