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 c65d0e793d Add support for `FrameEpoch` element inside the `DYNAMIC`
element.
c65d0e793d is described below
commit c65d0e793dc9370a846f0eb3d4316a4b65dda36a
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sun Sep 7 19:47:27 2025 +0200
Add support for `FrameEpoch` element inside the `DYNAMIC` element.
---
.../sis/coordinate/DefaultCoordinateMetadata.java | 2 +-
.../apache/sis/io/wkt/GeodeticObjectParser.java | 63 +++++++++++++++----
.../apache/sis/referencing/crs/AbstractCRS.java | 7 ++-
.../org/apache/sis/referencing/crs/DynamicCRS.java | 73 ++++++++++++++++++++++
.../referencing/factory/sql/EPSGDataAccess.java | 13 +---
.../org/apache/sis/referencing/internal/Epoch.java | 65 ++++++++++++++++++-
.../apache/sis/referencing/privy/WKTKeywords.java | 2 +
.../sis/io/wkt/GeodeticObjectParserTest.java | 31 +++++++++
.../apache/sis/referencing/internal/EpochTest.java | 21 +++++--
9 files changed, 243 insertions(+), 34 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/coordinate/DefaultCoordinateMetadata.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/coordinate/DefaultCoordinateMetadata.java
index 1aaa840548..74911cd66b 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/coordinate/DefaultCoordinateMetadata.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/coordinate/DefaultCoordinateMetadata.java
@@ -199,7 +199,7 @@ public class DefaultCoordinateMetadata extends
FormattableObject
protected String formatTo(final Formatter formatter) {
formatter.append(WKTUtilities.toFormattable(crs));
if (epoch != null) {
- formatter.append(new Epoch(epoch));
+ formatter.append(new Epoch(epoch, false));
}
return WKTKeywords.CoordinateMetadata;
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
index 2fda55c79c..96e320971d 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
@@ -32,6 +32,7 @@ import java.text.NumberFormat;
import java.text.ParsePosition;
import java.text.ParseException;
import java.time.Instant;
+import java.time.temporal.Temporal;
import static java.util.Collections.singletonMap;
import javax.measure.Unit;
import javax.measure.Quantity;
@@ -69,6 +70,7 @@ import
org.apache.sis.referencing.privy.EllipsoidalHeightCombiner;
import org.apache.sis.referencing.privy.AxisDirections;
import org.apache.sis.referencing.privy.WKTUtilities;
import org.apache.sis.referencing.privy.WKTKeywords;
+import org.apache.sis.referencing.internal.Epoch;
import org.apache.sis.referencing.internal.Legacy;
import org.apache.sis.referencing.internal.VerticalDatumTypes;
import org.apache.sis.referencing.internal.PositionalAccuracyConstant;
@@ -310,10 +312,10 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
&& null == (object = parseAxis (FIRST, element, null,
Units.METRE ))
&& null == (object = parsePrimeMeridian (FIRST, element,
false, Units.DEGREE))
&& null == (object = parseEnsemble (FIRST, element,
Datum.class, greenwich()))
- && null == (object = parseDatum (FIRST, element,
greenwich()))
+ && null == (object = parseDatum (FIRST, element,
greenwich(), null))
&& null == (object = parseEllipsoid (FIRST, element))
&& null == (object = parseToWGS84 (FIRST, element))
- && null == (object = parseVerticalDatum (FIRST, element,
false))
+ && null == (object = parseVerticalDatum (FIRST, element, null,
false))
&& null == (object = parseTimeDatum (FIRST, element))
&& null == (object = parseParametricDatum (FIRST, element))
&& null == (object = parseEngineeringDatum (FIRST, element,
false))
@@ -1409,6 +1411,23 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
}
}
+ /**
+ * Parses a {@code "FrameEoch"} (WKT 2) element.
+ *
+ * @param parent the parent element.
+ * @return the frame epoch, or {@code null} if none.
+ * @throws ParseException if the {@code "FrameEoch"} element cannot be
parsed.
+ */
+ private Temporal parseDynamic(final Element parent) throws ParseException {
+ final Element element = parent.pullElement(OPTIONAL,
WKTKeywords.Dynamic);
+ if (element == null) {
+ return null;
+ }
+ Temporal epoch = Epoch.fromYear(pullElementAsDouble(element,
WKTKeywords.FrameEpoch, MANDATORY), 0);
+ element.close(ignoredElements);
+ return epoch;
+ }
+
/**
* Parses an {@code "Ensemble"} (WKT 2) element.
*
@@ -1490,12 +1509,15 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
* @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link
#MANDATORY}.
* @param parent the parent element.
* @param meridian the prime meridian.
+ * @param epoch the frame epoch if the datum is dynamic, or {@code
null} if static.
* @return the {@code "Datum"} element as a {@link GeodeticDatum} object.
* @throws ParseException if the {@code "Datum"} element cannot be parsed.
*
* @see
org.apache.sis.referencing.datum.DefaultGeodeticDatum#formatTo(Formatter)
*/
- private GeodeticDatum parseDatum(final int mode, final Element parent,
final PrimeMeridian meridian) throws ParseException {
+ private GeodeticDatum parseDatum(final int mode, final Element parent,
final PrimeMeridian meridian, final Temporal epoch)
+ throws ParseException
+ {
final Element element = parent.pullElement(mode, WKTKeywords.Datum,
WKTKeywords.GeodeticDatum);
if (element == null) {
return null;
@@ -1509,7 +1531,10 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
}
final DatumFactory datumFactory = factories.getDatumFactory();
try {
- return datumFactory.createGeodeticDatum(properties, ellipsoid,
meridian);
+ if (epoch == null) {
+ return datumFactory.createGeodeticDatum(properties, ellipsoid,
meridian);
+ }
+ return datumFactory.createGeodeticDatum(properties, ellipsoid,
meridian, epoch);
} catch (FactoryException exception) {
throw element.parseFailed(exception);
}
@@ -1526,11 +1551,12 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
*
* @param mode {@link #FIRST}, {@link #OPTIONAL} or {@link #MANDATORY}.
* @param parent the parent element.
+ * @param epoch the frame epoch if the datum is dynamic, or {@code
null} if static.
* @param isWKT1 {@code true} if the parent is a WKT 1 element.
* @return the {@code "VerticalDatum"} element as a {@link VerticalDatum}
object.
* @throws ParseException if the {@code "VerticalDatum"} element cannot be
parsed.
*/
- private VerticalDatum parseVerticalDatum(final int mode, final Element
parent, final boolean isWKT1)
+ private VerticalDatum parseVerticalDatum(final int mode, final Element
parent, final Temporal epoch, final boolean isWKT1)
throws ParseException
{
final Element element = parent.pullElement(mode,
@@ -1548,9 +1574,13 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
if (method == null) {
method = VerticalDatumTypes.fromDatum(name, null, null);
}
+ final Map<String, Object> properties = parseAnchorAndClose(element,
name);
final DatumFactory datumFactory = factories.getDatumFactory();
try {
- return
datumFactory.createVerticalDatum(parseAnchorAndClose(element, name), method);
+ if (epoch == null) {
+ return datumFactory.createVerticalDatum(properties, method);
+ }
+ return datumFactory.createVerticalDatum(properties, method, epoch);
} catch (FactoryException exception) {
throw element.parseFailed(exception);
}
@@ -1942,8 +1972,9 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
if (meridian == null) {
meridian = greenwich();
}
+ final Temporal epoch = parseDynamic(element);
final DatumEnsemble<GeodeticDatum> ensemble =
parseEnsemble(OPTIONAL, element, GeodeticDatum.class, meridian);
- final GeodeticDatum datum = parseDatum(ensemble == null ?
MANDATORY : OPTIONAL, element, meridian);
+ final GeodeticDatum datum = parseDatum(ensemble == null ?
MANDATORY : OPTIONAL, element, meridian, epoch);
final IdentifiedObject datumOrEnsemble = (datum != null) ? datum :
ensemble;
final Map<String,?> properties = parseMetadataAndClose(element,
name, datumOrEnsemble);
if (cs instanceof EllipsoidalCS) {
// By far the most frequent case.
@@ -2016,8 +2047,9 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
}
}
if (baseCRS == null) { // The most usual case.
+ final Temporal epoch = parseDynamic(element);
ensemble = parseEnsemble(OPTIONAL, element, VerticalDatum.class,
null);
- datum = parseVerticalDatum(ensemble == null ? MANDATORY :
OPTIONAL, element, isWKT1);
+ datum = parseVerticalDatum(ensemble == null ? MANDATORY :
OPTIONAL, element, epoch, isWKT1);
}
final IdentifiedObject datumOrEnsemble = (datum != null) ? datum :
ensemble;
final CoordinateSystem cs;
@@ -2035,8 +2067,11 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
* The `parseVerticalDatum(…)` method may have been unable to
resolve the realization method.
* But sometimes the axis (which was not available when we
created the datum) provides
* more information. Verify if we can have a better type now,
and if so rebuild the datum.
+ *
+ * TODO: remove this hack. It is mostly for old standard. The
check for dynamic datum
+ * is a dirty trick for checking if we have a newer standard.
*/
- if (method == null && datum != null) {
+ if (method == null && datum != null && !(datum instanceof
DynamicReferenceFrame)) {
var type =
VerticalDatumTypes.fromDatum(datum.getName().getCode(), datum.getAlias(),
cs.getAxis(0));
if (type != null) {
final DatumFactory datumFactory =
factories.getDatumFactory();
@@ -2146,9 +2181,9 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
* In the latter case, the datum is null and we have instead
DerivingConversion element from a BaseParametricCRS.
*/
DatumEnsemble<ParametricDatum> ensemble = null;
- ParametricDatum datum = null;
- SingleCRS baseCRS = null;
- Conversion fromBase = null;
+ ParametricDatum datum = null;
+ SingleCRS baseCRS = null;
+ Conversion fromBase = null;
if (!isBaseCRS) {
/*
* UNIT[…] in DerivedCRS parameters are mandatory according ISO
19162 and the specification does not said
@@ -2349,7 +2384,9 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
number, AxisDirection.UNSPECIFIED, Units.UNITY);
}
final Map<String,Object> properties =
parseMetadataAndClose(element, name, baseCRS);
- final Map<String,Object> axisName =
singletonMap(CoordinateSystem.NAME_KEY, AxisDirections.appendTo(new
StringBuilder("CS"), axes));
+ final Map<String,Object> axisName = singletonMap(
+ CoordinateSystem.NAME_KEY,
+ AxisDirections.appendTo(new StringBuilder("CS"), axes));
final var derivedCS = new AbstractCS(axisName, axes);
/*
* Creates a derived CRS from the information found in a WKT 1
{@code FITTED_CS} element.
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java
index a30da11b1c..275ed71dfb 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java
@@ -490,9 +490,14 @@ public class AbstractCRS extends AbstractReferenceSystem
implements CoordinateRe
final Function<D, FormattableObject> toFormattable,
final Function<C, D> asDatum)
{
+ final boolean supportsDynamic =
formatter.getConvention().supports(Convention.WKT2_2019);
if (datum != null) {
+ if (supportsDynamic) {
+ formatter.append(DynamicCRS.createIfDynamic(datum));
+ formatter.newLine();
+ }
formatter.appendFormattable(datum, toFormattable);
- } else if (formatter.getConvention().supports(Convention.WKT2_2019)) {
+ } else if (supportsDynamic) {
formatter.appendFormattable(crs.getDatumEnsemble(),
DefaultDatumEnsemble::castOrCopy);
} else {
// Apply `toFormattable` unconditionally for forcing a conversion
of ensemble to datum.
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DynamicCRS.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DynamicCRS.java
new file mode 100644
index 0000000000..31bbc386b1
--- /dev/null
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DynamicCRS.java
@@ -0,0 +1,73 @@
+/*
+ * 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.crs;
+
+import java.time.temporal.Temporal;
+import org.opengis.referencing.datum.Datum;
+import org.apache.sis.io.wkt.Formatter;
+import org.apache.sis.io.wkt.FormattableObject;
+import org.apache.sis.referencing.internal.Epoch;
+import org.apache.sis.referencing.privy.WKTKeywords;
+
+// Specific to the geoapi-3.1 and geoapi-4.0 branches:
+import org.opengis.referencing.datum.DynamicReferenceFrame;
+
+
+/**
+ * An element inserted in the <abbr>WKT</abbr> formatting of dynamic
<abbr>CRS</abbr>.
+ *
+ * @todo {@code MODEL} sub-element is not yet supported.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+final class DynamicCRS extends FormattableObject {
+ /**
+ * The reference frame epoch.
+ */
+ private final Temporal epoch;
+
+ /**
+ * Creates a new element.
+ *
+ * @param epoch the reference frame epoch.
+ */
+ private DynamicCRS(final Temporal epoch) {
+ this.epoch = epoch;
+ }
+
+ /**
+ * Returns a {@code DYNAMIC} element for the given datum, or {@code null}
if the datum is not dynamic.
+ */
+ static DynamicCRS createIfDynamic(final Datum datum) {
+ if (datum instanceof DynamicReferenceFrame) {
+ return new DynamicCRS(((DynamicReferenceFrame)
datum).getFrameReferenceEpoch());
+ }
+ return null;
+ }
+
+ /**
+ * Formats this epoch as a <i>Well Known Text</i> {@code
CoordinateMetadata[…]} element.
+ *
+ * @param formatter the formatter where to format the inner content of
this WKT element.
+ * @return {@code "Dynamic"}.
+ */
+ @Override
+ protected String formatTo(final Formatter formatter) {
+ formatter.append(new Epoch(epoch, true));
+ return WKTKeywords.Dynamic;
+ }
+}
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 9e4474feb5..5cf8fe2fc9 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
@@ -45,7 +45,6 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.time.DateTimeException;
import java.time.LocalDate;
-import java.time.Year;
import java.time.temporal.Temporal;
import javax.measure.Unit;
import javax.measure.quantity.Angle;
@@ -87,6 +86,7 @@ import org.apache.sis.referencing.privy.CoordinateOperations;
import org.apache.sis.referencing.privy.ReferencingFactoryContainer;
import org.apache.sis.referencing.internal.DeferredCoordinateOperation;
import org.apache.sis.referencing.internal.DeprecatedCode;
+import org.apache.sis.referencing.internal.Epoch;
import org.apache.sis.referencing.internal.EPSGParameterDomain;
import org.apache.sis.referencing.internal.ParameterizedTransformBuilder;
import org.apache.sis.referencing.internal.PositionalAccuracyConstant;
@@ -965,16 +965,7 @@ public class EPSGDataAccess extends
GeodeticAuthorityFactory implements CRSAutho
* @throws SQLException if an error occurred while querying the database.
*/
private static Temporal getOptionalEpoch(final ResultSet result, final int
columnIndex) throws SQLException {
- final double epoch = getOptionalDouble(result, columnIndex);
- if (Double.isNaN(epoch)) {
- return null;
- }
- final var year = Year.of((int) epoch);
- final long day = Math.round((epoch - year.getValue()) * year.length());
- if (day == 0) {
- return year;
- }
- return year.atMonth(year.atDay(Math.toIntExact(day)).getMonth());
+ return Epoch.fromYear(getOptionalDouble(result, columnIndex), 0);
}
/**
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Epoch.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Epoch.java
index de6020319d..0c3f131d3d 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Epoch.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Epoch.java
@@ -17,6 +17,7 @@
package org.apache.sis.referencing.internal;
import java.time.Instant;
+import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.Year;
import java.time.YearMonth;
@@ -33,6 +34,7 @@ import org.apache.sis.util.privy.Constants;
/**
* Epoch of a coordinate set or of a dynamic reference frame.
* This is a temporary object used for Well-Known Text formatting.
+ * Contains also utility methods for conversion from/to temporal objects.
*
* @author Martin Desruisseaux (Geomatys)
*/
@@ -47,12 +49,19 @@ public final class Epoch extends FormattableObject {
*/
public final int precision;
+ /**
+ * Whether this epoch is the frame epoch instead of the coordinate epoch.
+ */
+ private final boolean frame;
+
/**
* Converts the given epoch to a fractional year.
*
* @param epoch the epoch to express as a fractional year.
+ * @param frame whether this epoch is the frame epoch instead of the
coordinate epoch
*/
- public Epoch(Temporal epoch) {
+ public Epoch(Temporal epoch, final boolean frame) {
+ this.frame = frame;
if (epoch instanceof Instant) {
epoch = OffsetDateTime.ofInstant((Instant) epoch, ZoneOffset.UTC);
}
@@ -77,15 +86,65 @@ public final class Epoch extends FormattableObject {
}
}
+ /**
+ * Returns the temporal object from the given year.
+ * The type of the returned object depends on the {@code precision}
argument:
+ *
+ * <li>
+ * <ul>= 0: returns {@link Year}, unless there is a fraction part in
which case returns {@link YearMonth}.</ul>
+ * <ul>≤ 2: returns {@link YearMonth}.</ul>
+ * <ul>≤ 3: returns {@link LocalDate}.</ul>
+ * <ul>Other cases not yet implemented, but may be in the future.</ul>
+ * </li>
+ *
+ * @param epoch the epoch as a fractional year.
+ * @param precision number of valid digits in the given epoch.
+ * @return the given epoch as a temporal object, or {@code null} if the
given value is NaN.
+ */
+ public static Temporal fromYear(final double epoch, final int precision) {
+ if (Double.isNaN(epoch)) {
+ return null;
+ }
+ Year year = Year.of((int) epoch);
+ double time = epoch - year.getValue();
+ long day = Math.round(time * year.length());
+ if (day == 0 && precision <= 0) return year;
+ final LocalDate date = year.atDay(Math.toIntExact(day + 1));
+ if (precision <= 2) return year.atMonth(date.getMonth());
+ return date;
+ }
+
+ /**
+ * Returns the temporal object from a year given as a string.
+ * The precision is determined by the number of digits that are explicitly
written, even if zero.
+ *
+ * @param epoch the epoch as a fractional year.
+ * @return the given epoch as a temporal object, or {@code null} if the
given value is NaN.
+ * @throws NumberFormatException if the given string cannot be parsed as
number.
+ */
+ public static Temporal fromYear(final String epoch) {
+ int precision = 0;
+ int i = epoch.indexOf('.');
+ if (i >= 0) {
+ final int length = epoch.length();
+ while (++i < length) {
+ final char c = epoch.charAt(i);
+ if (c < '0' || c > '9') break;
+ precision++;
+ }
+ }
+ return fromYear(Double.parseDouble(epoch), precision);
+ }
+
/**
* Formats this epoch as a <i>Well Known Text</i> {@code
CoordinateMetadata[…]} element.
*
* @param formatter the formatter where to format the inner content of
this WKT element.
- * @return {@code "Epoch"}.
+ * @return {@code "Epoch"} or {@code "FrameEpoch"}.
*/
@Override
protected String formatTo(final Formatter formatter) {
formatter.append(value, precision);
- return WKTKeywords.Epoch;
+ return frame ? WKTKeywords.FrameEpoch : WKTKeywords.Epoch;
}
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/WKTKeywords.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/WKTKeywords.java
index c2fe067df1..76bf6005cd 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/WKTKeywords.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/WKTKeywords.java
@@ -92,6 +92,7 @@ public final class WKTKeywords extends Static {
Ensemble = "Ensemble",
Member = "Member",
EnsembleAccuracy = "EnsembleAccuracy",
+ Dynamic = "Dynamic",
ToWGS84 = "ToWGS84";
/**
@@ -228,6 +229,7 @@ public final class WKTKeywords extends Static {
*/
public static final String
CoordinateMetadata = "CoordinateMetadata",
+ FrameEpoch = "FrameEpoch",
Epoch = "Epoch",
Point = "Point";
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
index 54790bb884..77c961fd07 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/io/wkt/GeodeticObjectParserTest.java
@@ -22,6 +22,8 @@ import java.util.Locale;
import java.time.Instant;
import java.text.ParsePosition;
import java.text.ParseException;
+import java.time.Year;
+import java.time.temporal.Temporal;
import javax.measure.Unit;
import javax.measure.quantity.Length;
import org.opengis.referencing.IdentifiedObject;
@@ -979,6 +981,35 @@ public final class GeodeticObjectParserTest extends
EPSGDependentTestCase {
"AXIS[" + axis + "]]");
}
+ /**
+ * Tests the parsing of a vertical <abbr>CRS</abbr>.
+ *
+ * @throws ParseException if the parsing failed.
+ */
+ @Test
+ public void testVerticalCRS() throws ParseException {
+ final VerticalCRS crs = parse(VerticalCRS.class,
+ "VerticalCRS[“RH2000 height”,\n" +
+ " Dynamic[FrameEpoch[2000]],\n" +
+ " VerticalDatum[“Rikets hojdsystem 2000”],\n" +
+ " CS[vertical, 1],\n" +
+ " Axis[“Gravity-related height (H)”, up],\n" +
+ " Unit[“metre”, 1],\n" +
+ " Usage[\n" +
+ " Scope[“Geodesy, engineering survey.”],\n" +
+ " Area[“Sweden - onshore.”],\n" +
+ " BBox[55.28, 10.93, 69.07, 24.17]],\n" +
+ " Id[“EPSG”, 5613, “12.013”,
URI[“urn:ogc:def:crs:EPSG:12.013:5613”]],\n" +
+ " Remark[“Replaces RH70 (CRS code 5718) from 2005.”]]");
+
+ assertNameAndIdentifierEqual("RH2000 height", 5613, crs);
+ assertNameAndIdentifierEqual("Rikets hojdsystem 2000", 0,
crs.getDatum());
+ Temporal epoch = assertInstanceOf(DynamicReferenceFrame.class,
crs.getDatum()).getFrameReferenceEpoch();
+ assertEquals(Year.of(2000), epoch);
+ assertEquals("Geodesy, engineering survey.",
getSingleton(crs.getDomains()).getScope().toString());
+ assertEquals("Replaces RH70 (CRS code 5718) from 2005.",
crs.getRemarks().orElseThrow().toString());
+ }
+
/**
* Returns the conversion from {@code north} to {@code south}.
*/
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/internal/EpochTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/internal/EpochTest.java
index 4cd3f1fbbf..5a0a91bfee 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/internal/EpochTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/internal/EpochTest.java
@@ -20,6 +20,7 @@ import java.time.Year;
import java.time.YearMonth;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.time.Month;
// Test dependencies
import org.junit.jupiter.api.Test;
@@ -46,7 +47,7 @@ public final class EpochTest extends TestCase {
*/
@Test
public void testYear() {
- var epoch = new Epoch(Year.of(2010));
+ var epoch = new Epoch(Year.of(2010), false);
assertEquals(2010, epoch.value);
assertEquals(0, epoch.precision);
assertEquals("Epoch[2010]", epoch.toString());
@@ -57,12 +58,12 @@ public final class EpochTest extends TestCase {
*/
@Test
public void testYearMonth() {
- var epoch = new Epoch(YearMonth.of(2016, 1));
+ var epoch = new Epoch(YearMonth.of(2016, 1), false);
assertEquals(2016, epoch.value);
assertEquals(2, epoch.precision);
assertEquals("Epoch[2016.00]", epoch.toString());
- epoch = new Epoch(YearMonth.of(2016, 7));
+ epoch = new Epoch(YearMonth.of(2016, 7), false);
assertEquals(2016.49726775956, epoch.value, 1E-11);
assertEquals(2, epoch.precision);
assertEquals("Epoch[2016.50]", epoch.toString());
@@ -73,7 +74,7 @@ public final class EpochTest extends TestCase {
*/
@Test
public void testLocalDate() {
- var epoch = new Epoch(LocalDate.of(2016, 7, 20));
+ var epoch = new Epoch(LocalDate.of(2016, 7, 20), false);
assertEquals(2016.54918032787, epoch.value, 1E-11);
assertEquals(3, epoch.precision);
assertEquals("Epoch[2016.549]", epoch.toString());
@@ -84,9 +85,19 @@ public final class EpochTest extends TestCase {
*/
@Test
public void testLocalDateTime() {
- var epoch = new Epoch(LocalDateTime.of(2014, 2, 15, 10, 40));
+ var epoch = new Epoch(LocalDateTime.of(2014, 2, 15, 10, 40), false);
assertEquals(2014.12450532725, epoch.value, 1E-11);
assertEquals(8, epoch.precision);
assertEquals("Epoch[2014.12450533]", epoch.toString());
}
+
+ /**
+ * Tests {@link Epoch#fromYear(String)}.
+ */
+ @Test
+ public void testFromYear() {
+ assertEquals( Year.of(2010),
Epoch.fromYear("2010"));
+ assertEquals(YearMonth.of(2010, Month.JANUARY),
Epoch.fromYear("2010.0"));
+ assertEquals(YearMonth.of(2010, Month.APRIL),
Epoch.fromYear("2010.3"));
+ }
}