sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: GridChange should round to the enclosing GridExtent instead than nearest. We added a GridRoundingMode enumeration for giving control.
Date Fri, 21 Dec 2018 15:18:25 GMT
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 2020033357e5f68cb3519f33ccc55b1c5b20028a
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Fri Dec 21 16:17:17 2018 +0100

    GridChange should round to the enclosing GridExtent instead than nearest. We added a GridRoundingMode
enumeration for giving control.
---
 .../org/apache/sis/coverage/grid/GridChange.java   | 44 +++++++++++-
 .../org/apache/sis/coverage/grid/GridExtent.java   | 82 +++++++++++++++-------
 .../org/apache/sis/coverage/grid/GridGeometry.java | 13 ++--
 .../apache/sis/coverage/grid/GridRoundingMode.java | 64 +++++++++++++++++
 .../apache/sis/coverage/grid/GridExtentTest.java   |  8 +--
 .../apache/sis/coverage/grid/GridGeometryTest.java |  4 +-
 .../java/org/apache/sis/math/SequenceVector.java   | 12 ++--
 .../src/main/java/org/apache/sis/math/Vector.java  |  6 +-
 8 files changed, 186 insertions(+), 47 deletions(-)

diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java
index 4566fea..1046fe9 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridChange.java
@@ -108,6 +108,8 @@ public class GridChange implements Serializable {
      *   <li>Target {@linkplain GridGeometry#getExtent() extent}.</li>
      * </ul>
      *
+     * This constructor assumes {@link GridRoundingMode#ENCLOSING} and no margin.
+     *
      * @param  source  the source grid geometry.
      * @param  target  the target grid geometry.
      * @throws IncompleteGridGeometryException if a "grid to CRS" conversion is not defined,
@@ -115,8 +117,44 @@ public class GridChange implements Serializable {
      * @throws TransformException if an error occurred during conversion from source to target.
      */
     public GridChange(final GridGeometry source, final GridGeometry target) throws TransformException
{
-        ArgumentChecks.ensureNonNull("source", source);
-        ArgumentChecks.ensureNonNull("target", target);
+        this(source, target, GridRoundingMode.ENCLOSING, null);
+    }
+
+    /**
+     * Creates a description of the conversion from given source grid geometry to given target
grid geometry.
+     * The following information are mandatory:
+     * <ul>
+     *   <li>Source {@linkplain GridGeometry#getExtent() extent}.</li>
+     *   <li>Source "{@linkplain GridGeometry#getGridToCRS(PixelInCell) grid to CRS}"
conversion.</li>
+     *   <li>Target "grid to CRS" conversion.</li>
+     * </ul>
+     *
+     * The following information are optional but recommended:
+     * <ul>
+     *   <li>Source coordinate reference system.</li>
+     *   <li>Target {@linkplain GridGeometry#getCoordinateReferenceSystem() coordinate
reference system}.</li>
+     *   <li>Target {@linkplain GridGeometry#getExtent() extent}.</li>
+     * </ul>
+     *
+     * An optional {@code margin} can be specified for increasing the size of the grid extent
computed by this constructor.
+     * For example if the caller wants to apply bilinear interpolations in an image, it will
needs 1 more pixel on each
+     * image border. If the caller wants to apply bi-cubic interpolations, it will needs
2 more pixels on each image border.
+     * If the {@code margin} array length is shorter than the target dimension, then zero
is assumed for all missing dimensions.
+     *
+     * @param  source     the source grid geometry.
+     * @param  target     the target grid geometry.
+     * @param  rounding   controls behavior of rounding from floating point values to integers.
+     * @param  margin     if non-null, expand the extent by that amount of cells on each
target dimension.
+     * @throws IncompleteGridGeometryException if a "grid to CRS" conversion is not defined,
+     *         or if the {@code source} does not specify an {@linkplain GridGeometry#getExtent()
extent}.
+     * @throws TransformException if an error occurred during conversion from source to target.
+     */
+    public GridChange(final GridGeometry source, final GridGeometry target, final GridRoundingMode
rounding, final int... margin)
+            throws TransformException
+    {
+        ArgumentChecks.ensureNonNull("source",   source);
+        ArgumentChecks.ensureNonNull("target",   target);
+        ArgumentChecks.ensureNonNull("rounding", rounding);
         if (source.equals(target)) {
             // Optimization for a common case.
             mapCorners = mapCenters = MathTransforms.identity(source.getDimension());
@@ -131,7 +169,7 @@ public class GridChange implements Serializable {
             final GridExtent domain = source.getExtent();
             mapCorners  = path(source, crsChange, target, PixelInCell.CELL_CORNER);
             mapCenters  = path(source, crsChange, target, PixelInCell.CELL_CENTER);
-            targetRange = new GridExtent(domain.toCRS(mapCorners, mapCenters), target.extent,
null);
+            targetRange = new GridExtent(domain.toCRS(mapCorners, mapCenters), rounding,
margin, target.extent, null);
             /*
              * Get an estimation of the scale factors when converting from source to target.
              * If all scale factors are 1, we will not store the array for consistency with
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index 0a5b984..e565d17 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -290,46 +290,78 @@ public class GridExtent implements Serializable {
      * <p><b>NOTE:</b> this constructor is not public because its contract
is a bit approximate.</p>
      *
      * @param  envelope            the envelope containing cell indices to store in a {@code
GridExtent}.
+     * @param  rounding            controls behavior of rounding from floating point values
to integers.
+     * @param  margin              if non-null, expand the extent by that amount of cells
on each envelope dimension.
      * @param  enclosing           if the new grid is a sub-grid of a larger grid, that larger
grid. Otherwise {@code null}.
      * @param  modifiedDimensions  if {@code enclosing} is non-null, the grid dimensions
to set from the envelope.
      *                             The length of this array shall be equal to the {@code
envelope} dimension.
      *
      * @see #toCRS(MathTransform, MathTransform)
      */
-    GridExtent(final AbstractEnvelope envelope, final GridExtent enclosing, final int[] modifiedDimensions)
{
+    GridExtent(final AbstractEnvelope envelope, final GridRoundingMode rounding, final int[]
margin,
+            final GridExtent enclosing, final int[] modifiedDimensions)
+    {
         final int dimension = envelope.getDimension();
         coordinates = (enclosing != null) ? enclosing.coordinates.clone() : allocate(dimension);
         for (int i=0; i<dimension; i++) {
             final double min = envelope.getLower(i);
             final double max = envelope.getUpper(i);
             if (min >= Long.MIN_VALUE && max <= Long.MAX_VALUE && min
<= max) {
-                long lower = Math.round(min);
-                long upper = Math.round(max);
-                if (lower != upper) upper--;                                // For making
the coordinate inclusive.
-                /*
-                 * The [lower … upper] range may be slightly larger than desired in some
rounding error situations.
-                 * For example if 'min' was 1.49999 and 'max' was 2.50001, the roundings
will create a [1…3] range
-                 * while there is actually only 2 pixels. We detect those rounding problems
by comparing the spans
-                 * before and after rounding. We attempt an adjustment only if the span mistmatch
is ±1, otherwise
-                 * the difference is assumed to be caused by overflow. On the three values
that can be affected by
-                 * the adjustment (min, max and span), we change only the number which is
farthest from an integer
-                 * value.
-                 */
-                long error = (upper - lower) + 1;                           // Negative number
if overflow.
-                if (error >= 0) {
-                    final double span = envelope.getSpan(i);
-                    final long extent = Math.round(span);
-                    if (extent != 0 && Math.abs(error -= extent) == 1) {
-                        final double dmin = Math.abs(min - Math.rint(min));
-                        final double dmax = Math.abs(max - Math.rint(max));
-                        final boolean adjustMax = (dmax >= dmin);
-                        if (Math.abs(span - extent) < (adjustMax ? dmax : dmin)) {
-                            if (adjustMax) upper = Math.subtractExact(upper, error);
-                            else lower = Math.addExact(lower, error);
+                long lower, upper;
+                switch (rounding) {
+                    default: {
+                        throw new AssertionError(rounding);
+                    }
+                    case ENCLOSING: {
+                        lower = (long) Math.floor(min);
+                        upper = (long) Math.ceil (max);
+                        if (lower != upper) upper--;                                // For
making the coordinate inclusive.
+                        break;
+                    }
+                    case NEAREST: {
+                        lower = Math.round(min);
+                        upper = Math.round(max);
+                        if (lower != upper) upper--;                                // For
making the coordinate inclusive.
+                        /*
+                         * The [lower … upper] range may be slightly larger than desired
in some rounding error situations.
+                         * For example if 'min' was 1.49999 and 'max' was 2.50001,  the rounding
will create a [1…3] range
+                         * while there is actually only 2 pixels. We detect those rounding
problems by comparing the spans
+                         * before and after rounding.  We attempt an adjustment only if the
span mismatch is ±1, otherwise
+                         * the difference is assumed to be caused by overflow. On the three
values that can be affected by
+                         * the adjustment (min, max and span), we change only the number
which is farthest from an integer
+                         * value.
+                         */
+                        long error = (upper - lower) + 1;                           // Negative
number if overflow.
+                        if (error >= 0) {
+                            final double span = envelope.getSpan(i);
+                            final long extent = Math.round(span);
+                            if (extent != 0 && Math.abs(error -= extent) == 1) {
+                                final double dmin = Math.abs(min - Math.rint(min));
+                                final double dmax = Math.abs(max - Math.rint(max));
+                                final boolean adjustMax = (dmax >= dmin);
+                                if (Math.abs(span - extent) < (adjustMax ? dmax : dmin))
{
+                                    if (adjustMax) upper = Math.subtractExact(upper, error);
+                                    else lower = Math.addExact(lower, error);
+                                }
+                            }
                         }
                     }
                 }
                 /*
+                 * If the user specified a margin, add it now. The margin dimension indices
follow the envelope
+                 * dimension indices.  Note that the resulting extent will be intersected
with enclosing extent
+                 * at the next step, which may cancel the margin effect.
+                 */
+                if (margin != null && i < margin.length) {
+                    final int m = margin[i];
+                    lower = Math.subtractExact(lower, m);
+                    upper = Math.addExact(upper, m);
+                }
+                if (lower > upper) {
+                    upper += (lower - upper) >>> 1;         // (upper - lower) as
unsigned integer: overflow-safe.
+                    lower = upper;
+                }
+                /*
                  * At this point the grid range has been computed (lower to upper).
                  * Update the coordinates accordingly.
                  */
@@ -559,7 +591,7 @@ public class GridExtent implements Serializable {
      *                      If different, then this is assumed to map pixel centers instead
than pixel corners.
      * @return this grid extent in real world coordinates.
      *
-     * @see #GridExtent(AbstractEnvelope, GridExtent, int[])
+     * @see #GridExtent(AbstractEnvelope, GridRoundingMode, int[], GridExtent, int[])
      */
     final GeneralEnvelope toCRS(final MathTransform cornerToCRS, final MathTransform gridToCRS)
throws TransformException {
         final int dimension = getDimension();
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
index ce7db8a..cccd6dc 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
@@ -354,7 +354,7 @@ public class GridGeometry implements Serializable {
      * coordinates used in {@link GridExtent}.
      * The rules for deciding whether coordinates should be rounded toward nearest integers,
      * to {@linkplain Math#floor(double) floor} or to {@linkplain Math#ceil(double) ceil}
values
-     * are implementation details and may be adjusted in any Apache SIS versions.</p>
+     * are specified by the {@link GridRoundingMode} argument.</p>
      *
      * <p>Because of the uncertainties explained in above paragraph, this constructor
should be used only in last resort,
      * when the grid extent is unknown. For determinist results, developers should prefer
the
@@ -373,22 +373,26 @@ public class GridGeometry implements Serializable {
      * @param  envelope   the geospatial envelope, including its coordinate reference system
if available.
      *                    There is no guarantees that the envelope actually stored in the
{@code GridGeometry}
      *                    will be equal to this specified envelope.
+     * @param  rounding   controls behavior of rounding from floating point values to integers.
      * @throws TransformException if the math transform can not compute the grid extent or
the resolution.
      */
     @SuppressWarnings("null")
-    public GridGeometry(final PixelInCell anchor, final MathTransform gridToCRS, final Envelope
envelope) throws TransformException {
+    public GridGeometry(final PixelInCell anchor, final MathTransform gridToCRS, final Envelope
envelope,
+            final GridRoundingMode rounding) throws TransformException
+    {
         if (gridToCRS == null) {
             ArgumentChecks.ensureNonNull("envelope", envelope);
         } else if (envelope != null) {
             ensureDimensionMatches("envelope", gridToCRS.getTargetDimensions(), envelope.getDimension());
         }
+        ArgumentChecks.ensureNonNull("rounding", rounding);
         this.gridToCRS   = PixelTranslation.translate(gridToCRS, anchor, PixelInCell.CELL_CENTER);
         this.cornerToCRS = PixelTranslation.translate(gridToCRS, anchor, PixelInCell.CELL_CORNER);
         Matrix scales = MathTransforms.getMatrix(gridToCRS);
         int numToIgnore = 1;
         if (envelope != null && cornerToCRS != null) {
             GeneralEnvelope env = Envelopes.transform(cornerToCRS.inverse(), envelope);
-            extent = new GridExtent(env, null, null);
+            extent = new GridExtent(env, GridRoundingMode.NEAREST, null, null, null);
             env = extent.toCRS(cornerToCRS, gridToCRS);         // 'gridToCRS' specified
by the user, not 'this.gridToCRS'.
             env.setCoordinateReferenceSystem(envelope.getCoordinateReferenceSystem());
             this.envelope = new ImmutableEnvelope(env);
@@ -607,7 +611,8 @@ public class GridGeometry implements Serializable {
         } catch (FactoryException e) {
             throw new TransformException(Resources.format(Resources.Keys.CanNotMapToGridDimensions),
e);
         }
-        final GridExtent sub = new GridExtent(Envelopes.transform(gridToAOI.inverse(), areaOfInterest),
extent, modifiedDimensions);
+        final GridExtent sub = new GridExtent(Envelopes.transform(gridToAOI.inverse(), areaOfInterest),
+                GridRoundingMode.NEAREST, null, extent, modifiedDimensions);
         return sub.equals(extent) ? extent : sub;
     }
 
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridRoundingMode.java
b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridRoundingMode.java
new file mode 100644
index 0000000..529f43d
--- /dev/null
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridRoundingMode.java
@@ -0,0 +1,64 @@
+/*
+ * 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.coverage.grid;
+
+
+/**
+ * Specifies rounding behavior of computations of {@link GridExtent} from floating-point
values.
+ * The rounding mode controls how real numbers are converted into {@code GridExtent}'s
+ * {@linkplain GridExtent#getLow(int) low}, {@linkplain GridExtent#getHigh(int) high} and
+ * {@linkplain GridExtent#getSize(int) size} integer values.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+public enum GridRoundingMode {
+    /**
+     * Converts grid low, high and size to nearest integer values.
+     * This mode applies the following steps:
+     *
+     * <ol>
+     *   <li>Floating point values are converted to integers with {@link Math#round(double)}.</li>
+     *   <li>On the three integer values ({@linkplain GridExtent#getLow(int) low},
{@linkplain GridExtent#getHigh(int) high}
+     *       and {@linkplain GridExtent#getSize(int) size}) obtained after rounding, add
or subtract 1 to the value which is
+     *       farthest from an integer value in order to keep unchanged the two values that
are closer to integers.</li>
+     * </ol>
+     *
+     * <div class="note"><b>Example:</b>
+     * the [<var>low</var> … <var>high</var>] range may be slightly
larger than desired in some rounding error situations.
+     * For example if <var>low</var> before rounding was 1.49999 and <var>high</var>
before rounding was 2.50001, then the
+     * range after rounding will be [1…3] while the expected size is actually only 2 pixels.
This {@code NEAREST} rounding
+     * mode detects those rounding issues by comparing the <var>size</var> before
and after rounding. In this example, the
+     * size is 2.00002 pixels, which is closer to an integer value than the <var>low</var>
and <var>high</var> values.
+     * Consequently this {@code NEAREST} mode will rather adjust <var>low</var>
or <var>high</var> (depending which one is
+     * farthest from integer values) in order to keep <var>size</var> at its
closest integer value, which is 2.</div>
+     */
+    NEAREST,
+
+    /**
+     * Converts grid low and high to values that fully encloses the envelope.
+     * This mode applies the following steps:
+     *
+     * <ul>
+     *   <li>Grid {@linkplain GridExtent#getLow(int) low} are converted to integers
with {@link Math#floor(double)}.</li>
+     *   <li>Grid {@linkplain GridExtent#getHigh(int) high} are converted to integers
with {@link Math#ceil(double)}.</li>
+     * </ul>
+     */
+    ENCLOSING
+}
diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
index 068d993..3519a8d 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridExtentTest.java
@@ -46,14 +46,14 @@ public final strictfp class GridExtentTest extends TestCase {
     }
 
     /**
-     * Tests the {@link GridExtent#GridExtent(AbstractEnvelope, GridExtent, int[])} constructor.
+     * Tests the {@link GridExtent#GridExtent(AbstractEnvelope, GridRoundingMode, int[],
GridExtent, int[])} constructor.
      */
     @Test
     public void testCreateFromEnvelope() {
         final GeneralEnvelope env = new GeneralEnvelope(HardCodedCRS.IMAGE);
         env.setRange(0, -23.01, 30.107);
         env.setRange(1,  12.97, 18.071);
-        GridExtent extent = new GridExtent(env, null, null);
+        GridExtent extent = new GridExtent(env, GridRoundingMode.NEAREST, null, null, null);
         assertExtentEquals(extent, 0, -23, 29);
         assertExtentEquals(extent, 1,  13, 17);
         assertEquals(DimensionNameType.COLUMN, extent.getAxisType(0).get());
@@ -61,7 +61,7 @@ public final strictfp class GridExtentTest extends TestCase {
     }
 
     /**
-     * Tests the rounding performed by the {@link GridExtent#GridExtent(AbstractEnvelope,
GridExtent, int[])} constructor.
+     * Tests the rounding performed by the {@link GridExtent#GridExtent(AbstractEnvelope,
GridRoundingMode, int[], GridExtent, int[])} constructor.
      */
     @Test
     public void testRoundings() {
@@ -71,7 +71,7 @@ public final strictfp class GridExtentTest extends TestCase {
         env.setRange(2, 1.49998, 3.50001);      // Round to [1…4), stored as [1…2] (not
[1…3]) because the span is close to 2.
         env.setRange(3, 1.49999, 3.50002);      // Round to [1…4), stored as [2…3] because
the upper part is closer to integer.
         env.setRange(4, 1.2,     3.8);          // Round to [1…4), stores as [1…3] because
the span is not close enough to integer.
-        GridExtent extent = new GridExtent(env, null, null);
+        GridExtent extent = new GridExtent(env, GridRoundingMode.NEAREST, null, null, null);
         assertExtentEquals(extent, 0, 1, 2);
         assertExtentEquals(extent, 1, 1, 2);
         assertExtentEquals(extent, 2, 1, 2);
diff --git a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
index 47148f5..c6ebd8f 100644
--- a/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java
@@ -206,7 +206,7 @@ public final strictfp class GridGeometryTest extends TestCase {
             0,   0.5, -90,
             0.5, 0,  -180,
             0,   0,     1));
-        final GridGeometry grid = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, envelope);
+        final GridGeometry grid = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, envelope,
GridRoundingMode.NEAREST);
         assertExtentEquals(
                 new long[] {370, 40},
                 new long[] {389, 339}, grid.getExtent());
@@ -237,7 +237,7 @@ public final strictfp class GridGeometryTest extends TestCase {
             0.5, 0,   0, -180,
             0,   0,   2,    3,
             0,   0,   0,    1));
-        final GridGeometry grid = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, envelope);
+        final GridGeometry grid = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, envelope,
GridRoundingMode.NEAREST);
         assertExtentEquals(
                 new long[] {336,  20,  4},
                 new long[] {401, 419, 10}, grid.getExtent());
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java b/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
index c707a04..0efa2b7 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/SequenceVector.java
@@ -22,6 +22,7 @@ import org.apache.sis.measure.NumberRange;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.Numerics;
 
 
 /**
@@ -29,7 +30,7 @@ import org.apache.sis.util.resources.Errors;
  * Values may be {@code long} or {@code double} types.
  *
  * @author  Martin Desruisseaux (MPO, Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.8
  * @module
  */
@@ -203,8 +204,8 @@ abstract class SequenceVector extends Vector implements Serializable {
         }
 
         /** Returns the increment between all consecutive values */
-        @Override public Number increment(final double tolerance) {
-            return Numbers.wrap(increment, type);
+        @Override public final Number increment(final double tolerance) {
+            return Numerics.valueOf(increment);         // Always Double even if data type
is Float.
         }
 
         /** Computes the minimal and maximal values in this vector. */
@@ -252,11 +253,6 @@ abstract class SequenceVector extends Vector implements Serializable
{
         @Override public String stringValue(final int index) {
             return String.valueOf(floatValue(index));
         }
-
-        /** Returns the increment between all consecutive values */
-        @Override public Number increment(final double tolerance) {
-            return (float) increment;
-        }
     }
 
 
diff --git a/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java b/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
index 90b1fe4..2a6ff93 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/math/Vector.java
@@ -714,7 +714,11 @@ search:     for (;;) {
                 double inc = (doubleValue(--i) - first) / i;                            
 // First estimation of increment.
                 final int pz = Math.max(0, Math.min(i, (int) Math.rint(-first / inc))); 
 // Presumed index of value zero.
                 if (doubleValue(pz) == 0) {
-                    inc = (pz == i) ? -doubleValue(pz-1) : doubleValue(pz+1);   // Presumed
less subject to rounding errors.
+                    final Number value = (pz == i) ? get(pz-1) : get(pz+1);             
 // Value adjacent to zero.
+                    if (!(value instanceof Float)) {
+                        inc = value.doubleValue();                              // Presumed
less subject to rounding errors.
+                        if (pz == i) inc = -inc;
+                    }
                 }
                 if (type == Numbers.FLOAT) {
                     while (i >= 1) {


Mime
View raw message