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 dc743d4a4d9a75ba2f9d6ee9ee070912eab2afb8 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Wed Aug 3 18:40:56 2022 +0200 More robust check for wraparound in `GridEvaluator` when the given point has less dimensions than the grid geometry. --- .../org/apache/sis/gui/map/ValuesUnderCursor.java | 1 + .../apache/sis/coverage/grid/GridEvaluator.java | 37 ++++++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java index 8a8a1e0c2a..d618731a48 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/ValuesUnderCursor.java @@ -318,6 +318,7 @@ public abstract class ValuesUnderCursor { } evaluator = coverage.forConvertedValues(true).evaluator(); evaluator.setNullIfOutside(true); + evaluator.setWraparoundEnabled(true); canvas(property).ifPresent((c) -> setSlice(c.getSliceExtent())); if (previous != null && bands.equals(previous.getSampleDimensions())) { // Same configuration than previous coverage. diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java index 5e22a9d539..a13ca901d3 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridEvaluator.java @@ -183,7 +183,7 @@ public class GridEvaluator implements GridCoverage.Evaluator { /** * Returns the default slice where to perform evaluation, or an empty map if unspecified. * Keys are dimensions from 0 inclusive to {@link GridGeometry#getDimension()} exclusive, - * and values are the grid coordinate of the slice in that dimension. + * and values are the grid coordinates of the slice in the dimension specified by the key. * * <p>This information allows to invoke {@link #apply(DirectPosition)} with for example two-dimensional points * even if the underlying coverage is three-dimensional. The missing coordinate values are replaced by the @@ -204,7 +204,7 @@ public class GridEvaluator implements GridCoverage.Evaluator { /** * Sets the default slice where to perform evaluation when the points do not have enough dimensions. - * A {@code null} argument restore the default value, which is to infer the slice from the coverage + * A {@code null} argument restores the default value, which is to infer the slice from the coverage * grid geometry. * * @param slice the default slice where to perform evaluation, or an empty map if none. @@ -214,6 +214,7 @@ public class GridEvaluator implements GridCoverage.Evaluator { * * @since 1.3 */ + @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public void setDefaultSlice(Map<Integer,Long> slice) { if (!Objects.equals(this.slice, slice)) { if (slice != null) { @@ -475,7 +476,6 @@ public class GridEvaluator implements GridCoverage.Evaluator { * If most cases, the work of this method ends here. The remaining code in this method * is for handling wraparound axes. If a coordinate is outside the coverage extent, * check if a wraparound on some axes would bring the coordinates inside the extent. - * The first step is to get the point closest to the extent. */ long axes = wraparoundAxes; if (axes != 0) { @@ -487,6 +487,12 @@ public class GridEvaluator implements GridCoverage.Evaluator { final double c = coordinates[i]; double border; if (c < (border = wraparoundExtent[j++]) || c > (border = wraparoundExtent[j])) { + /* + * Detected that the point is outside the grid extent along an axis where wraparound is possible. + * The first time that we find such axis, expand the coordinates array for storing two points. + * The two points will have the same coordinates, except on all axes where the point is outside. + * On those axes, the coordinate of the first point is set to the closest border of the grid. + */ if (outsideAxes == 0) { final int n = coordinates.length; coordinates = Arrays.copyOf(coordinates, 2*Math.max(n, gridToWraparound.getTargetDimensions())); @@ -502,7 +508,10 @@ public class GridEvaluator implements GridCoverage.Evaluator { /* * If a coordinate was found outside the grid, transform to a CRS where we can apply shift. * It may be the same CRS than the coverage CRS or the source CRS, but not necessarily. - * Current version does not try to optimize by checking if `point` argument can be reused. + * For example if the CRS is projected, then we need to use a geographic intermediate CRS. + * In the common case where the source CRS is already geographic, the second point in the + * `coordinates` array after `transform(…)` will contain the same coordinates as `point`, + * but potentially with more dimensions. */ if (outsideAxes != 0) { gridToWraparound.transform(coordinates, 0, coordinates, 0, 2); @@ -515,7 +524,7 @@ public class GridEvaluator implements GridCoverage.Evaluator { * then round that shift to an integer amount of periods. Modify the original * coordinate by applying that modified translation. */ - final int oi = i + s; + final int oi = i + s; // Index of original coordinates. double shift = coordinates[i] - coordinates[oi]; shift = Math.copySign(Math.ceil(Math.abs(shift) / period), shift) * period; coordinates[oi] += shift; @@ -536,7 +545,23 @@ public class GridEvaluator implements GridCoverage.Evaluator { } outsideAxes &= ~(1L << i); } while (outsideAxes != 0); - System.arraycopy(coordinates, 0, position.coordinates, 0, position.coordinates.length); + /* + * Copy shifted coordinate values to the final `FractionalGridCoordinates`, except the NaN values. + * NaN values may exist if the given `point` has less dimensions than the grid geometry, in which + * case missing values have been replaced by `slice` values in the `target` array but not in the + * `coordinates` array. We want to keep the `slice` values in the `target` array. + * + * TODO: to be strict, we should skip the copy only if `slice.containsKey(i)` is true, because it + * could happen that a transform resulted in NaN values in other dimensions. But that check would + * be costly, so we avoid it for now. + */ + final double[] target = position.coordinates; + for (int i = target.length; --i >= 0;) { + final double value = coordinates[i]; + if (!Double.isNaN(value)) { + target[i] = value; + } + } } } return position;