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 0218c71  Take in account the case where the temporal origin of a 
dataset has fractional seconds.
0218c71 is described below

commit 0218c7138eab2767f5479f5d108fba6ea010e075
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Mar 2 14:19:59 2019 +0100

    Take in account the case where the temporal origin of a dataset has 
fractional seconds.
---
 .../sis/referencing/crs/DefaultTemporalCRS.java    | 44 +++++++++++++++++++--
 .../referencing/crs/DefaultTemporalCRSTest.java    | 46 +++++++++++++++++++---
 .../sis/internal/util/StandardDateFormat.java      |  6 +--
 3 files changed, 84 insertions(+), 12 deletions(-)

diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
index 0a041d4..03072d9 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultTemporalCRS.java
@@ -26,6 +26,7 @@ import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.measure.quantity.Time;
 import javax.measure.UnitConverter;
+import javax.measure.Unit;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.cs.TimeCS;
 import org.opengis.referencing.crs.TemporalCRS;
@@ -36,6 +37,7 @@ import org.apache.sis.internal.metadata.MetadataUtilities;
 import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.io.wkt.Formatter;
 import org.apache.sis.measure.Units;
+import org.apache.sis.math.Fraction;
 
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
 import static org.apache.sis.internal.util.StandardDateFormat.NANOS_PER_SECOND;
@@ -218,8 +220,19 @@ public class DefaultTemporalCRS extends AbstractCRS 
implements TemporalCRS {
      * Initialize the fields required for {@link #toInstant(double)} and 
{@link #toValue(Instant)} operations.
      */
     private void initializeConverter() {
-        origin    = datum.getOrigin().getTime() / MILLIS_PER_SECOND;
-        toSeconds = 
super.getCoordinateSystem().getAxis(0).getUnit().asType(Time.class).getConverterTo(Units.SECOND);
+        toSeconds = getUnit().getConverterTo(Units.SECOND);
+        long t = datum.getOrigin().getTime();
+        origin = t / MILLIS_PER_SECOND;
+        t %= MILLIS_PER_SECOND;
+        if (t != 0) {
+            /*
+             * The origin is usually an integer amount of days or hours. It 
rarely has a fractional amount of seconds.
+             * If it happens anyway, put the fractional amount of seconds in 
the converter instead than adding another
+             * field in this class for such very rare situation. Accuracy 
should be okay since the offset is small.
+             */
+            UnitConverter c = Units.converter(null, new Fraction((int) t, 
MILLIS_PER_SECOND).simplify());
+            toSeconds = c.concatenate(toSeconds);
+        }
     }
 
     /**
@@ -261,6 +274,27 @@ public class DefaultTemporalCRS extends AbstractCRS 
implements TemporalCRS {
     }
 
     /**
+     * Returns the unit of measurement of temporal measurement in the 
coordinate reference system.
+     * This is a convenience method for {@link 
org.opengis.referencing.cs.CoordinateSystemAxis#getUnit()}
+     * on the unique axis of this coordinate reference system. The unit of 
measurement returned by this method
+     * is the unit of the value expected in argument by {@link 
#toInstant(double)} and {@link #toDate(double)},
+     * and the unit of the value returned by {@code toValue(…)} methods.
+     *
+     * <div class="note"><b>Implementation note:</b>
+     * this method is declared final and does not invoke overridden {@link 
#getCoordinateSystem()} method
+     * because this {@code getUnit()} method is invoked indirectly by 
constructors. Another reason is that
+     * the overriding point is the {@code CoordinateSystemAxis.getUnit()} 
method and we want to avoid
+     * introducing another overriding point that could be inconsistent with 
above method.</div>
+     *
+     * @return the temporal unit of measurement of coordinates in this CRS.
+     *
+     * @since 1.0
+     */
+    public final Unit<Time> getUnit() {
+        return 
super.getCoordinateSystem().getAxis(0).getUnit().asType(Time.class);
+    }
+
+    /**
      * {@inheritDoc}
      *
      * @return {@inheritDoc}
@@ -283,7 +317,7 @@ public class DefaultTemporalCRS extends AbstractCRS 
implements TemporalCRS {
      * If the given value {@linkplain Double#isNaN is NaN} or infinite, then 
this method returns {@code null}.
      * This method is the converse of {@link #toValue(Instant)}.
      *
-     * @param  value  a value in this axis unit.
+     * @param  value  a value in this axis. Unit of measurement is given by 
{@link #getUnit()}.
      * @return the value as an instant, or {@code null} if the given value is 
NaN or infinite.
      *
      * @since 1.0
@@ -307,7 +341,7 @@ public class DefaultTemporalCRS extends AbstractCRS 
implements TemporalCRS {
      * <p>This method is provided for interoperability with legacy {@code 
java.util.Date} object.
      * New code should use {@link #toInstant(double)} instead.</p>
      *
-     * @param  value  a value in this axis unit.
+     * @param  value  a value in this axis unit. Unit of measurement is given 
by {@link #getUnit()}.
      * @return the value as a {@linkplain Date date}, or {@code null} if the 
given value is NaN or infinite.
      */
     public Date toDate(double value) {
@@ -330,6 +364,7 @@ public class DefaultTemporalCRS extends AbstractCRS 
implements TemporalCRS {
      *
      * @param  time  the value as an instant, or {@code null}.
      * @return the value in this axis unit, or {@link Double#NaN} if the given 
instant is {@code null}.
+     *         Unit of measurement is given by {@link #getUnit()}.
      *
      * @since 1.0
      */
@@ -353,6 +388,7 @@ public class DefaultTemporalCRS extends AbstractCRS 
implements TemporalCRS {
      *
      * @param  time  the value as a {@linkplain Date date}, or {@code null}.
      * @return the value in this axis unit, or {@link Double#NaN} if the given 
time is {@code null}.
+     *         Unit of measurement is given by {@link #getUnit()}.
      */
     public double toValue(final Date time) {
         if (time != null) {
diff --git 
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultTemporalCRSTest.java
 
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultTemporalCRSTest.java
index 5729528..9b07d48 100644
--- 
a/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultTemporalCRSTest.java
+++ 
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/crs/DefaultTemporalCRSTest.java
@@ -18,11 +18,16 @@ package org.apache.sis.referencing.crs;
 
 import java.time.Instant;
 import java.util.Date;
+import java.util.Collections;
+import org.apache.sis.referencing.datum.DefaultTemporalDatum;
+import org.apache.sis.referencing.cs.HardCodedCS;
 import org.apache.sis.io.wkt.Convention;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static org.apache.sis.test.MetadataAssert.*;
+import static 
org.apache.sis.internal.util.StandardDateFormat.MILLISECONDS_PER_DAY;
+import static 
org.apache.sis.internal.util.StandardDateFormat.NANOS_PER_MILLISECOND;
 
 
 /**
@@ -82,12 +87,43 @@ public final strictfp class DefaultTemporalCRSTest extends 
TestCase {
      */
     @Test
     public void testDateConversion() {
-        final double  value   = 58543.25;                               // 3 
march 2019.
-        final Date    date    = HardCodedCRS.TIME.toDate(value);
-        final Instant instant = HardCodedCRS.TIME.toInstant(value);
+        final DefaultTemporalCRS crs = HardCodedCRS.TIME;
+        final double  value   = 58543.25;                               // 
2019-03-01T06:00:00Z
+        final Date    date    = crs.toDate(value);
+        final Instant instant = crs.toInstant(value);
         assertEquals("toInstant", Instant.ofEpochSecond(1551420000L), instant);
         assertEquals("toDate",    instant, date.toInstant());
-        assertEquals("toValue",   value, HardCodedCRS.TIME.toValue(instant), 
STRICT);
-        assertEquals("toValue",   value, HardCodedCRS.TIME.toValue(date), 
STRICT);
+        assertEquals("toValue",   value, crs.toValue(instant), STRICT);
+        assertEquals("toValue",   value, crs.toValue(date), STRICT);
+    }
+
+    /**
+     * Same as {@link #testDateConversion()} but with a nanosecond component.
+     * Fraction of seconds need to be handled in special way by current 
implementation.
+     */
+    @Test
+    public void testDateConversionWithNanos() {
+        final DefaultTemporalDatum datum = new DefaultTemporalDatum(
+                Collections.singletonMap(DefaultTemporalDatum.NAME_KEY, "For 
test"),
+                new Date(10000L * MILLISECONDS_PER_DAY + 12345));              
          // 1997-05-19T00:00:12.345Z
+        final DefaultTemporalCRS crs = new DefaultTemporalCRS(
+                Collections.singletonMap(DefaultTemporalCRS.NAME_KEY, 
datum.getName()),
+                datum, HardCodedCS.DAYS);
+        /*
+         * DefaultTemporalCRS.toSeconds converter should have a non-zero 
offset because of the 0.345 seconds offset
+         * in temporal datum. Ask for a date two days after the origin and 
verify that the 12.345 part is missing.
+         */
+        final double ε = 1E-15;
+        assertEquals(2 - 12.345 / (60*60*24), crs.toValue(new Date(10002L * 
MILLISECONDS_PER_DAY)),  ε);
+        assertEquals(2 - 12.345 / (60*60*24), 
crs.toValue(Instant.ofEpochSecond(10002L*(60*60*24))), ε);
+        /*
+         * Add a millisecond component and test again.
+         */
+        final Instant t = Instant.ofEpochSecond(10002L*(60*60*24) + 15, 789 * 
NANOS_PER_MILLISECOND);
+        final double  v = 2 + (15.789 - 12.345) / (60*60*24);
+        assertEquals("toValue",   v,            crs.toValue(t), ε);
+        assertEquals("toValue",   v,            crs.toValue(Date.from(t)), ε);
+        assertEquals("toInstant", t,            crs.toInstant(v));
+        assertEquals("toDate",    Date.from(t), crs.toDate(v));
     }
 }
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/StandardDateFormat.java
 
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/StandardDateFormat.java
index f347c34..86ed80d 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/internal/util/StandardDateFormat.java
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/StandardDateFormat.java
@@ -232,17 +232,17 @@ replace:    if (Character.isWhitespace(c)) {
     /**
      * Number of milliseconds in one second.
      */
-    public static final long MILLIS_PER_SECOND = 1000;
+    public static final int MILLIS_PER_SECOND = 1000;
 
     /**
      * Number of nanoseconds in one millisecond.
      */
-    public static final long NANOS_PER_MILLISECOND = 1000000;
+    public static final int NANOS_PER_MILLISECOND = 1000000;
 
     /**
      * Number of nanoseconds in one second.
      */
-    public static final long NANOS_PER_SECOND = 1000000000;
+    public static final int NANOS_PER_SECOND = 1000000000;
 
     /**
      * Converts the given legacy {@code Date} object into a {@code java.time} 
implementation in given timezone.

Reply via email to