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 76bdc396c1 Resole the problem documented in the `TODO` comment of
`GridDerivation`: When a wraparound may happen, the computation of envelope
unions need to be done between compatible envelopes (computed with the same
wraparound shift).
76bdc396c1 is described below
commit 76bdc396c1aea5b5505a47be512559b9f584454b
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sun Aug 3 17:21:05 2025 +0200
Resole the problem documented in the `TODO` comment of `GridDerivation`:
When a wraparound may happen, the computation of envelope unions need to be
done between compatible envelopes (computed with the same wraparound shift).
---
.../apache/sis/coverage/grid/GridDerivation.java | 32 ++--
.../org/apache/sis/coverage/grid/GridExtent.java | 9 +-
.../main/org/apache/sis/geometry/Envelopes.java | 102 ++++++++----
.../apache/sis/geometry/WraparoundInEnvelope.java | 171 ++++++++++++++++++++-
.../main/org/apache/sis/io/wkt/Formatter.java | 2 +-
.../sis/parameter/DefaultParameterDescriptor.java | 2 +
.../main/org/apache/sis/parameter/Verifier.java | 79 ++++++----
.../referencing/operation/provider/Wraparound.java | 2 +-
.../operation/transform/MolodenskyTransform.java | 2 +-
.../operation/transform/WraparoundTransform.java | 4 +-
.../org/apache/sis/geometry/EnvelopesTest.java | 12 +-
11 files changed, 331 insertions(+), 86 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
index c3087896bc..a1932913f5 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridDerivation.java
@@ -16,6 +16,7 @@
*/
package org.apache.sis.coverage.grid;
+import java.util.Map;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
@@ -40,6 +41,7 @@ import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.geometry.Envelopes;
import org.apache.sis.geometry.WraparoundAdjustment;
import org.apache.sis.feature.internal.Resources;
+import org.apache.sis.parameter.Parameters;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.CharSequences;
@@ -612,26 +614,30 @@ public class GridDerivation {
}
nowraparound = finder.gridToGrid();
/*
- * Converts the grid extent of the area of interest to the
grid coordinates of the base.
- * We may get one or two envelopes, depending on whether there
is a longitude wraparound.
- * If the area of interest has less dimensions than the base
grid, we may need to compute
- * more envelopes for enclosing the full span of dimensions
that are not in `areaOfInterest`.
+ * At this point, the transform between the coordinate systems
of the two grids is known.
+ * The `gridToGrid` transform is the main one. If the user did
not specified an AOI for
+ * all dimensions, then `gridToGrid` is for low coordinates
and `gridToGridHigh` is for
+ * high coordinates.
*/
final GeneralEnvelope[] envelopes;
if (gridToGrid.isIdentity() && (gridToGridHigh == null ||
gridToGridHigh.isIdentity())) {
envelopes = new GeneralEnvelope[]
{domain.toEnvelope(tight)};
} else {
- envelopes = domain.toEnvelopes(gridToGrid, tight,
nowraparound, null);
+ /*
+ * Converts the grid extent of the area of interest to the
grid coordinates of the base.
+ * We may get one or two envelopes, depending on whether
there is a longitude wraparound.
+ * If the area of interest has less dimensions than the
base grid, we may need to compute
+ * more envelopes for enclosing the full span of
dimensions that are not in `areaOfInterest`.
+ */
+ final Map<Parameters, GeneralEnvelope> transformed =
+ domain.toEnvelopes(gridToGrid, tight,
nowraparound, null);
if (gridToGridHigh != null) {
- final GeneralEnvelope[] high =
domain.toEnvelopes(gridToGridHigh, tight, nowraparoundHigh, null);
- for (int i = Math.min(envelopes.length, high.length);
--i >= 0;) {
- /*
- * TODO: actually, we have no guarantee that the
envelopes at the same index match.
- * We need to find a more reliable algorithm,
maybe by checking intersection first.
- */
- envelopes[i].add(high[i]);
- }
+ domain.toEnvelopes(gridToGridHigh, tight,
nowraparoundHigh, null).forEach((key, value) -> {
+ GeneralEnvelope previous =
transformed.putIfAbsent(key, value);
+ if (previous != null) previous.add(value);
+ });
}
+ envelopes =
transformed.values().toArray(GeneralEnvelope[]::new);
}
setBaseExtentClipped(tight, envelopes);
if (baseExtent != base.extent && baseExtent.equals(domain)) {
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
index 072ed3aada..5afcbfa8b5 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java
@@ -62,6 +62,7 @@ import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.matrix.MatrixSIS;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.referencing.operation.transform.TransformSeparator;
+import org.apache.sis.parameter.Parameters;
import org.apache.sis.math.MathFunctions;
import org.apache.sis.io.TableAppender;
import org.apache.sis.system.Modules;
@@ -1288,12 +1289,12 @@ public class GridExtent implements GridEnvelope,
LenientComparable, Serializable
* @see #GridExtent(AbstractEnvelope, GridRoundingMode, int[], GridExtent,
int[])
* @see GridGeometry#getEnvelope(CoordinateReferenceSystem)
*/
- final GeneralEnvelope[] toEnvelopes(final MathTransform gridToCRS, final
boolean isCenter,
- final MathTransform specified, final
Envelope fallback)
+ final Map<Parameters, GeneralEnvelope> toEnvelopes(final MathTransform
gridToCRS, final boolean isCenter,
+ final MathTransform
specified, final Envelope fallback)
throws TransformException
{
- final GeneralEnvelope[] envelopes = Envelopes.wraparound(gridToCRS,
toEnvelope(isCenter));
- for (final GeneralEnvelope envelope : envelopes) {
+ final Map<Parameters, GeneralEnvelope> envelopes =
Envelopes.transformWithWraparound(gridToCRS, toEnvelope(isCenter));
+ for (final GeneralEnvelope envelope : envelopes.values()) {
complete(envelope, specified, (specified == gridToCRS) ==
isCenter, fallback);
}
return envelopes;
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/Envelopes.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/Envelopes.java
index 9c29373152..15776e3034 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/Envelopes.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/Envelopes.java
@@ -22,10 +22,10 @@ package org.apache.sis.geometry;
* support Java2D (e.g. Android), or applications that do not need it may
want to avoid to
* force installation of the Java2D module (e.g. JavaFX/SWT).
*/
+import java.util.Map;
import java.util.Set;
-import java.util.List;
import java.util.Optional;
-import java.util.ArrayList;
+import java.util.LinkedHashMap;
import java.util.ConcurrentModificationException;
import java.util.logging.Logger;
import java.time.Instant;
@@ -44,10 +44,12 @@ import
org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.util.FactoryException;
import org.opengis.metadata.extent.GeographicBoundingBox;
import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
+import org.apache.sis.metadata.privy.ReferencingServices;
+import org.apache.sis.parameter.Parameters;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
-import org.apache.sis.metadata.privy.ReferencingServices;
+import org.apache.sis.referencing.operation.transform.WraparoundTransform;
import org.apache.sis.referencing.privy.CoordinateOperations;
import org.apache.sis.referencing.privy.DirectPositionView;
import org.apache.sis.referencing.privy.TemporalAccessor;
@@ -368,8 +370,8 @@ public final class Envelopes extends Static {
}
/**
- * Shared implementation of {@link #transform(MathTransform, Envelope)}
- * and {@link #wraparound(MathTransform, Envelope)} public methods.
+ * Shared implementation of {@link #transform(MathTransform, Envelope)} and
+ * {@link #transformWithWraparound(MathTransform, Envelope)} public
methods.
* Offers also the opportunity to save the transformed center coordinates.
*
* @param transform the transform to use.
@@ -382,7 +384,7 @@ public final class Envelopes extends Static {
* @return the transformed envelope. May be {@code null} if {@code
results} was non-null.
*/
private static GeneralEnvelope transform(final MathTransform transform,
final Envelope envelope,
- double[] targetPt, final List<GeneralEnvelope> results) throws
TransformException
+ double[] targetPt, final Map<Parameters, GeneralEnvelope> results)
throws TransformException
{
if (transform.isIdentity()) {
/*
@@ -580,7 +582,8 @@ nextPoint: for (int pointIndex = 0;;) { //
Break condition at th
* `GridExtent` have algorithms for completing empty envelopes.
*/
if (results != null) {
- results.add(transformed);
+ final GeneralEnvelope e = results.putIfAbsent(wc.state(),
transformed);
+ if (e != null) e.add(transformed); // Should never happen,
but let be safe.
transformed = null;
}
} while (wc.translate());
@@ -954,9 +957,8 @@ poles: for (int i=0; i<dimension; i++) {
*
* <h4>Limitation</h4>
* This method cannot handle the case where the envelope contains the
North or South pole.
- * Envelopes crossing the ±180° longitude are handled only if the given
transform contains
- * {@link
org.apache.sis.referencing.operation.transform.WraparoundTransform} steps;
- * this method does not add such steps itself.
+ * Furthermore, envelopes crossing the ±180° longitude are handled only if
the given transform
+ * contains {@link WraparoundTransform} steps, as this method does not add
such steps itself.
* For a more robust envelope transformation, use {@link
#transform(CoordinateOperation, Envelope)} instead.
*
* @param transform the transform to use.
@@ -977,38 +979,84 @@ poles: for (int i=0; i<dimension; i++) {
/**
* Transforms potentially many times an envelope using the given math
transform.
- * If the given envelope is {@code null}, then this method returns an
empty envelope.
- * Otherwise, if the transform does not contain any
- * {@link
org.apache.sis.referencing.operation.transform.WraparoundTransform} step,
- * then this method is equivalent to {@link #transform(MathTransform,
Envelope)} returned in an array of length 1.
- * Otherwise, this method returns many transformed envelopes where each
envelope describes approximately the same region.
- * If the envelope CRS is geographic, the many envelopes are the same
envelope shifted by 360° of longitude.
- * If the envelope CRS is projected, then the 360° shifts are applied
before the map projection.
- * It may result in very different coordinates.
+ * If the given envelope is {@code null}, then this method returns an
empty map.
+ * Otherwise, if the given transform does not contain any {@link
WraparoundTransform} step,
+ * then this method is equivalent to {@link #transform(MathTransform,
Envelope)} returned in a singleton map.
+ * Otherwise, this method returns many transformed envelopes where each
envelope describes approximately the
+ * same region. For example:
+ *
+ * <ul>
+ * <li>If the envelope <abbr>CRS</abbr> is geographic,
+ * the map values are the same envelope shifted by 360° of
longitude.</li>
+ * <li>If the envelope <abbr>CRS</abbr> is projected, then the 360°
shifts are applied
+ * before the map projection. It may result in very different
coordinates.</li>
+ * </ul>
+ *
+ * The keys identify which translations were applied on wraparound axes
for computing the associated envelope.
+ * For example, a key may identify an envelope computed with a shift of
360° of longitude on some coordinates,
+ * and another key may identify the same envelope but computed with a
shift of −360° of longitude.
+ * The content of those keys is implementation dependent and users should
not rely on the exact parameters.
+ * The main contract is that, for envelopes computed with the same
transform,
+ * the values that are associated with {@linkplain Object#equals(Object)
equal} keys
+ * were computed with wraparounds applied in the same way (e.g. +360°
versus −360°).
+ *
+ * <p><b>Example:</b> the union of two envelopes taking in account
wraparounds can be computed as below:</p>
+ *
+ * {@snippet lang="java" :
+ * MathTransform transform = ...;
+ * Map<Parameters, GeneralEnvelope> result =
Envelopes.transformWithWraparound(transform, envelope1);
+ * Envelopes.transformWithWraparound(transform,
envelope2).forEach((key, value) -> {
+ * GeneralEnvelope previous = result.putIfAbsent(key, value);
+ * if (previous != null) previous.add(value); // Envelope union.
+ * });
+ * }
+ *
+ * Note that the key may be {@code null} if the given transform
+ * does not contain any {@link WraparoundTransform} step.
*
* @param transform the transform to use.
* @param envelope envelope to transform, or {@code null}. This
envelope will not be modified.
- * @return the transformed envelopes, or an empty array if {@code
envelope} was null.
+ * @return the transformed envelopes, or an empty map if {@code envelope}
was null.
* @throws TransformException if a transform failed.
*
* @see #transform(MathTransform, Envelope)
- * @see org.apache.sis.referencing.operation.transform.WraparoundTransform
+ * @see WraparoundTransform
*
- * @since 1.3
+ * @since 1.5
*/
- public static GeneralEnvelope[] wraparound(final MathTransform transform,
final Envelope envelope)
- throws TransformException
+ public static Map<Parameters, GeneralEnvelope> transformWithWraparound(
+ final MathTransform transform, final Envelope envelope) throws
TransformException
{
ArgumentChecks.ensureNonNull("transform", transform);
if (envelope == null) {
- return new GeneralEnvelope[0];
+ return Map.of();
}
- final var results = new ArrayList<GeneralEnvelope>(4);
+ final var results = new LinkedHashMap<Parameters, GeneralEnvelope>(4);
final GeneralEnvelope transformed = transform(transform, envelope,
null, results);
if (results.isEmpty() && transformed != null) {
- return new GeneralEnvelope[] {transformed};
+ results.put(null, transformed);
}
- return results.toArray(GeneralEnvelope[]::new);
+ return results;
+ }
+
+ /**
+ * Transforms potentially many times an envelope using the given math
transform.
+ * If the given envelope is {@code null}, then this method returns an
empty array.
+ *
+ * @param transform the transform to use.
+ * @param envelope envelope to transform, or {@code null}. This
envelope will not be modified.
+ * @return the transformed envelopes, or an empty array if {@code
envelope} was null.
+ * @throws TransformException if a transform failed.
+ *
+ * @since 1.3
+ *
+ * @deprecated Replaced by {@link #transformWithWraparound(MathTransform,
Envelope)}.
+ */
+ @Deprecated(since = "1.5", forRemoval = true)
+ public static GeneralEnvelope[] wraparound(final MathTransform transform,
final Envelope envelope)
+ throws TransformException
+ {
+ return transformWithWraparound(transform,
envelope).values().toArray(GeneralEnvelope[]::new);
}
/**
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/WraparoundInEnvelope.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/WraparoundInEnvelope.java
index bfff4b5f29..9e22f41a24 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/WraparoundInEnvelope.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/WraparoundInEnvelope.java
@@ -16,10 +16,22 @@
*/
package org.apache.sis.geometry;
+import java.util.List;
import java.util.function.UnaryOperator;
+import org.opengis.parameter.GeneralParameterValue;
+import org.opengis.parameter.ParameterValue;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.ParameterNotFoundException;
import org.opengis.referencing.operation.MathTransform;
-import org.apache.sis.referencing.operation.transform.WraparoundTransform;
import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.privy.Numerics;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.referencing.operation.provider.Wraparound;
+import org.apache.sis.referencing.operation.transform.WraparoundTransform;
/**
@@ -74,12 +86,16 @@ final class WraparoundInEnvelope extends
WraparoundTransform {
/**
* Number of cycles at the {@linkplain #sourceMedian} position. This is
the minimum or maximum number
* of cycles to remove to a coordinate, depending if that coordinate is
before or after the median.
+ *
+ * <p>This value is an integer, but stored as a {@code double} for
avoiding type conversions.
+ * This is initialized at construction time, then changed when {@link
#translate()} is invoked.</p>
*/
private double limit;
/**
* The minimum and maximum number of {@linkplain #period period}s that
{@link #shift(double)} wanted
* to add to the coordinate before to be constrained to the {@link #limit}.
+ * This value is an integer, but stored as a {@code double} for avoiding
type conversions.
*/
private double minCycles, maxCycles;
@@ -197,7 +213,7 @@ final class WraparoundInEnvelope extends
WraparoundTransform {
if (!Double.isFinite(transform.sourceMedian)) {
return transform;
}
- final WraparoundInEnvelope w = new WraparoundInEnvelope(transform);
+ final var w = new WraparoundInEnvelope(transform);
if (wraparounds == null) {
wraparounds = new WraparoundInEnvelope[] {w};
} else {
@@ -226,5 +242,156 @@ final class WraparoundInEnvelope extends
WraparoundTransform {
}
return modified;
}
+
+ /**
+ * Returns a snapshot of the state of the wraparound transform.
+ * This state can change when {@link #translate()} is invoked.
+ * It may be used as a key in a map.
+ *
+ * @return a snapshot of the state of the wraparound transform.
+ */
+ final Parameters state() {
+ State state = null;
+ if (wraparounds != null) {
+ for (final WraparoundInEnvelope tr : wraparounds) {
+ state = new State(tr, state);
+ }
+ }
+ return state;
+ }
+ }
+
+ /**
+ * A semi-opaque object that describes the state of the wraparound
transform.
+ * The parameters published by this object are not committed API and may
change in any version.
+ * The current implementation is a linked list, but this list usually
contains only one element.
+ *
+ * <p>The purpose of this class is to be used as keys in a hash map.
Therefore, the only important
+ * methods are {@link #hashCode()} and {@link #equals(Object)}. The other
methods are defined for
+ * compliance with the {@link Parameters} contract, but should not be
used.</p>
+ */
+ private static final class State extends Parameters {
+ /** The group of parameters published by the public methods. */
+ private static volatile ParameterDescriptorGroup parameters;
+
+ /** Copy of a value from the enclosing wraparound transform. */
+ private final int wraparoundDimension;
+
+ /** Copies of values from the enclosing wraparound transform. */
+ private final double period, limit;
+
+ /** State of the wraparound transform executed before this one. */
+ private final State previous;
+
+ /** Creates a snapshot of the state of the given transforms. */
+ State(final WraparoundInEnvelope tr, final State previous) {
+ wraparoundDimension = tr.wraparoundDimension;
+ this.period = tr.period;
+ this.limit = tr.limit;
+ this.previous = previous;
+ }
+
+ /**
+ * Returns a hash code value for this state.
+ * It includes the hash code of previous {@code State} instances in
the linked list.
+ */
+ @Override
+ public int hashCode() {
+ int hash = wraparoundDimension;
+ State s1 = this;
+ do hash = 37*hash + (31*Double.hashCode(s1.limit) +
Double.hashCode(s1.period));
+ while ((s1 = s1.previous) != null);
+ return hash;
+ }
+
+ /**
+ * Compares this state with the given object for equality.
+ * The previous elements of the linked list are also compared.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof State) {
+ State s1 = this;
+ State s2 = (State) obj;
+ while (Numerics.equals(s1.limit, s2.limit) &&
+ Numerics.equals(s1.period, s2.period) &&
+ s1.wraparoundDimension == s2.wraparoundDimension)
+ {
+ s1 = s1.previous;
+ s2 = s2.previous;
+ if (s1 == s2) return true; // We are mostly interrested
the case when both are null.
+ if (s1 == null || s2 == null) break;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the group of parameters published by the object.
+ * Provided for compliance with the interface contract, but
+ * not useful for the purpose of the {@code State} object.
+ */
+ @Override
+ public ParameterDescriptorGroup getDescriptor() {
+ ParameterDescriptorGroup p = parameters;
+ if (p == null) {
+ final var builder = new
ParameterBuilder().setCodeSpace(Citations.SIS, "SIS");
+ final ParameterDescriptor<Double> shift =
builder.addName("shift").create(Double.NaN, null);
+ parameters = p = builder.addName("Wraparound
state").createGroup(Wraparound.WRAPAROUND_DIMENSION, shift);
+ }
+ return p;
+ }
+
+ /**
+ * Returns the values of all parameters defined by {@link
#getDescriptor()}.
+ * Provided for compliance with the interface contract, but* not
useful for
+ * the purpose of the {@code State} object.
+ */
+ @Override
+ public List<GeneralParameterValue> values() {
+ return List.of(parameter("wraparound_dim"), parameter("shift"));
+ }
+
+ /**
+ * Returns the value of the parameter of the given name.
+ * Provided for compliance with the interface contract,
+ * but* not useful for the purpose of {@code State}.
+ */
+ @Override
+ public ParameterValue<?> parameter(String name) {
+ switch (name) {
+ case "wraparound_dim": {
+ ParameterValue<Integer> p =
Wraparound.WRAPAROUND_DIMENSION.createValue();
+ p.setValue(wraparoundDimension);
+ return p;
+ }
+ case "shift": {
+ var p = (ParameterValue<?>)
getDescriptor().descriptors().get(1).createValue();
+ p.setValue(period * limit);
+ return p;
+ }
+ default: throw new ParameterNotFoundException(null, name);
+ }
+ }
+
+ /**
+ * Unsupported operation. Actually, we could return the parameters of
a previous element
+ * of the linked list. But this is not implemented yet because we have
no known usage.
+ */
+ @Override
+ public List<ParameterValueGroup> groups(String name) {
+ throw new ParameterNotFoundException(null, name);
+ }
+
+ /**
+ * Unsupported operation as this parameter group is unmodifiable.
+ */
+ @Override
+ public ParameterValueGroup addGroup(String name) {
+ throw new ParameterNotFoundException(null, name);
+ }
}
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
index 25629c7250..513ac1491a 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
@@ -1907,7 +1907,7 @@ public class Formatter implements Localized {
}
/**
- * Adds a warning for for an exception that occurred while fetching an
optional property.
+ * Adds a warning for an exception that occurred while fetching an
optional property.
*
* @param e the exception that occurred.
* @param element WKT keyword of the element where the exception occurred.
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptor.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptor.java
index ed2c0ffc10..04612d322a 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptor.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/DefaultParameterDescriptor.java
@@ -395,6 +395,7 @@ public class DefaultParameterDescriptor<T> extends
AbstractParameterDescriptor i
@Override
@SuppressWarnings("unchecked")
public Comparable<T> getMinimumValue() {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
final Range<?> valueDomain = this.valueDomain;
return (valueDomain != null && valueDomain.getElementType() ==
valueClass)
? (Comparable<T>) valueDomain.getMinValue() : null;
@@ -414,6 +415,7 @@ public class DefaultParameterDescriptor<T> extends
AbstractParameterDescriptor i
@Override
@SuppressWarnings("unchecked")
public Comparable<T> getMaximumValue() {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
final Range<?> valueDomain = this.valueDomain;
return (valueDomain != null && valueDomain.getElementType() ==
valueClass)
? (Comparable<T>) valueDomain.getMaxValue() : null;
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Verifier.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Verifier.java
index 93f911f797..3f7191b59f 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Verifier.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Verifier.java
@@ -38,6 +38,7 @@ import org.apache.sis.measure.Units;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.resources.IndexedResourceBundle;
/**
@@ -59,11 +60,6 @@ final class Verifier {
*/
private final boolean internal;
- /**
- * {@code true} if the last element in {@link #arguments} shall be set to
the erroneous value.
- */
- private final boolean needsValue;
-
/**
* The arguments to be used with the error message to format.
* The current implementation relies on the following invariants:
@@ -71,7 +67,7 @@ final class Verifier {
* <ul>
* <li>The first element in this array will be the parameter name.
Before the name is known,
* this element is either {@code null} or the index to append to the
name.</li>
- * <li>The last element shall be set to the erroneous value if {@link
#needsValue} is {@code true}.</li>
+ * <li>The last element may be set to the erroneous value.</li>
* </ul>
*/
private final Object[] arguments;
@@ -79,11 +75,10 @@ final class Verifier {
/**
* Stores information about an error.
*/
- private Verifier(final boolean internal, final short errorKey, final
boolean needsValue, final Object... arguments) {
- this.errorKey = errorKey;
- this.internal = internal;
- this.needsValue = needsValue;
- this.arguments = arguments;
+ private Verifier(final boolean internal, final short errorKey, final
Object... arguments) {
+ this.errorKey = errorKey;
+ this.internal = internal;
+ this.arguments = arguments;
}
/**
@@ -257,7 +252,7 @@ final class Verifier {
final Object[] arguments;
final Object minValue = valueDomain.getMinValue();
if ((minValue instanceof Number) && ((Number)
minValue).doubleValue() == 0 && !valueDomain.isMinIncluded()
- && (value instanceof Number) && ((Number)
value).doubleValue() < 0)
+ && (value instanceof Number) && ((Number)
value).doubleValue() <= 0)
{
errorKey = Errors.Keys.ValueNotGreaterThanZero_2;
arguments = new Object[2];
@@ -266,10 +261,10 @@ final class Verifier {
arguments = new Object[4];
arguments[1] = minValue;
arguments[2] = valueDomain.getMaxValue();
- }
+ }
if (isArray) arguments[0] = i;
arguments[arguments.length - 1] = value;
- return new Verifier(false, errorKey, true, arguments);
+ return new Verifier(false, errorKey, arguments);
}
}
}
@@ -288,27 +283,30 @@ final class Verifier {
* @param convertedValue the value <em>converted to the units specified
by the descriptor</em>.
* This is not necessarily the user-provided value.
*/
- @SuppressWarnings("unchecked")
- private static <T> Verifier ensureValidValue(final Class<T> valueClass,
final Set<T> validValues,
- final Comparable<T> minimum, final Comparable<T> maximum, final
Object convertedValue)
+ @SuppressWarnings({"unchecked", "element-type-mismatch"})
+ private static <T> Verifier ensureValidValue(final Class<T> valueClass,
+ final Set<T> validValues,
+ final Comparable<T> minimum,
+ final Comparable<T> maximum,
+ final Object convertedValue)
{
if (!valueClass.isInstance(convertedValue)) {
return new Verifier(true,
Resources.Keys.IllegalParameterValueClass_3,
- false, null, valueClass, convertedValue.getClass());
+ null, valueClass, convertedValue.getClass());
}
if (validValues != null && !validValues.contains(convertedValue)) {
- return new Verifier(true, Resources.Keys.IllegalParameterValue_2,
true, null, convertedValue);
+ return new Verifier(true, Resources.Keys.IllegalParameterValue_2,
null, convertedValue);
}
if ((minimum != null && minimum.compareTo((T) convertedValue) > 0) ||
(maximum != null && maximum.compareTo((T) convertedValue) < 0))
{
- return new Verifier(false, Errors.Keys.ValueOutOfRange_4, true,
null, minimum, maximum, convertedValue);
+ return new Verifier(false, Errors.Keys.ValueOutOfRange_4, null,
minimum, maximum, convertedValue);
}
return null;
}
/**
- * Converts the information about an "value out of range" error. The range
in the error message will be formatted
+ * Converts the information about a "value out of range" error. The range
in the error message will be formatted
* in the unit given by the user, which is not necessarily the same as the
unit of the parameter descriptor.
*
* @param converter the conversion from user unit to descriptor unit, or
{@code null} if none.
@@ -317,12 +315,12 @@ final class Verifier {
private void convertRange(UnitConverter converter) {
if (converter != null && !internal && errorKey ==
Errors.Keys.ValueOutOfRange_4) {
converter = converter.inverse();
- Object minimumValue = arguments[1];
- Object maximumValue = arguments[2];
- minimumValue = (minimumValue != null) ?
converter.convert(((Number) minimumValue).doubleValue()) : "−∞";
- maximumValue = (maximumValue != null) ?
converter.convert(((Number) maximumValue).doubleValue()) : "∞";
- arguments[1] = minimumValue;
- arguments[2] = maximumValue;
+ for (int i=1; i<=2; i++) {
+ Object value = arguments[i];
+ if (value instanceof Number) {
+ arguments[i] = converter.convert(((Number)
value).doubleValue());
+ }
+ }
}
}
@@ -356,10 +354,31 @@ final class Verifier {
value = Array.get(value, (Integer) index);
}
arguments[0] = name;
- if (needsValue) {
- arguments[arguments.length - 1] = value;
+ final IndexedResourceBundle resources;
+ if (internal) {
+ resources = Resources.forProperties(properties);
+ switch (errorKey) {
+ case Resources.Keys.IllegalParameterValue_2: {
+ arguments[1] = value;
+ break;
+ }
+ }
+ } else {
+ resources = Errors.forProperties(properties);
+ switch (errorKey) {
+ case Errors.Keys.ValueOutOfRange_4: {
+ if (arguments[1] == null) arguments[1] = "−∞";
+ if (arguments[2] == null) arguments[2] = "∞";
+ arguments[3] = value;
+ break;
+ }
+ case Errors.Keys.ValueNotGreaterThanZero_2: {
+ arguments[1] = value;
+ break;
+ }
+ }
}
- return (internal ? Resources.forProperties(properties) :
Errors.forProperties(properties)).getString(errorKey, arguments);
+ return resources.getString(errorKey, arguments);
}
/**
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Wraparound.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Wraparound.java
index 2dfe8da087..7322091b9e 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Wraparound.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/provider/Wraparound.java
@@ -76,7 +76,7 @@ public final class Wraparound extends AbstractProvider {
public static final ParameterDescriptor<Integer> WRAPAROUND_DIMENSION;
/**
- * The operation parameter descriptor for for the period.
+ * The operation parameter descriptor for the period.
*
* <!-- Generated by ParameterNameTableGenerator -->
* <table class="sis">
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
index 02e88a5827..beb7e1aea8 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/MolodenskyTransform.java
@@ -346,7 +346,7 @@ public class MolodenskyTransform extends
DatumShiftTransform {
* Prepare two affine transforms to be executed before and after the
MolodenskyTransform:
*
* - A "normalization" transform for converting degrees to radians,
- * - A "denormalization" transform for for converting radians to
degrees.
+ * - A "denormalization" transform for converting radians to degrees.
*/
context.normalizeGeographicInputs(0);
context.denormalizeGeographicOutputs(0);
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/WraparoundTransform.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/WraparoundTransform.java
index 23d31dc2cf..cc82827fdb 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/WraparoundTransform.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/WraparoundTransform.java
@@ -68,7 +68,7 @@ import org.apache.sis.parameter.Parameters;
* @author Martin Desruisseaux (Geomatys)
* @version 1.5
*
- * @see org.apache.sis.geometry.Envelopes#wraparound(MathTransform, Envelope)
+ * @see
org.apache.sis.geometry.Envelopes#transformWithWraparound(MathTransform,
Envelope)
*
* @since 1.1
*/
@@ -108,7 +108,7 @@ public class WraparoundTransform extends
AbstractMathTransform implements Serial
* then {@code sourceMedian} should be 180° (the value at the center of [0
… 360]° range).
* The value may be {@link Double#NaN} if unknown.
*
- * <p>This field is used for inverse transforms only; it has no effect on
the forward transforms.
+ * <p>This field is used for inverse transforms only. It has no effect on
the forward transforms.
* If not NaN, this value is used for building the transform returned by
{@link #inverse()}.</p>
*
* <h4>Design note</h4>
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/EnvelopesTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/EnvelopesTest.java
index 23624aec8d..91cff34382 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/EnvelopesTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/EnvelopesTest.java
@@ -31,6 +31,7 @@ import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransform2D;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.measure.Range;
+import org.apache.sis.parameter.Parameters;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.crs.DefaultCompoundCRS;
import org.apache.sis.referencing.cs.AxesConvention;
@@ -309,7 +310,7 @@ public final class EnvelopesTest extends
TransformTestCase<GeneralEnvelope> {
}
/**
- * Tests {@link Envelopes#wraparound(MathTransform, Envelope)}.
+ * Tests {@link Envelopes#transformWithWraparound(MathTransform,
Envelope)}.
*
* @throws TransformException if a coordinate transformation failed.
*/
@@ -320,10 +321,11 @@ public final class EnvelopesTest extends
TransformTestCase<GeneralEnvelope> {
envelope.setRange(1, 5, 9);
final MathTransform tr = WraparoundTransform.create(2, 0, 360, -180,
0);
assertTrue(tr instanceof WraparoundTransform);
- final GeneralEnvelope[] results = Envelopes.wraparound(tr, envelope);
- assertEquals(2, results.length);
- assertEnvelopeEquals(new GeneralEnvelope(new double[] {-200, 5}, new
double[] {-100, 9}), results[0]);
- assertEnvelopeEquals(new GeneralEnvelope(new double[] { 160, 5}, new
double[] { 260, 9}), results[1]);
+ final Map<Parameters, GeneralEnvelope> results =
Envelopes.transformWithWraparound(tr, envelope);
+ assertEquals(2, results.size());
+ final GeneralEnvelope[] envelopes =
results.values().toArray(GeneralEnvelope[]::new);
+ assertEnvelopeEquals(new GeneralEnvelope(new double[] {-200, 5}, new
double[] {-100, 9}), envelopes[0]);
+ assertEnvelopeEquals(new GeneralEnvelope(new double[] { 160, 5}, new
double[] { 260, 9}), envelopes[1]);
}
/**