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 a6bfe6ed64fea1d1dffe7e5b299ee84a3d2d1b5e Author: Martin Desruisseaux <[email protected]> AuthorDate: Wed Dec 29 04:49:52 2021 +0100 Fix a bug in the application of longitude wraparound when the objective CRS is different than the source CRS. The bug caused the image to disapear at some zoom levels. --- .../sis/internal/map/coverage/RenderingData.java | 13 ++++- .../internal/referencing/WraparoundApplicator.java | 57 +++++++++++++--------- .../sis/internal/referencing/package-info.java | 2 +- 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java index 42fd292..bff4c87 100644 --- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java +++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java @@ -418,6 +418,7 @@ public class RenderingData implements Cloneable { /** * Returns the position at the center of source data, or {@code null} if none. + * The coordinates are expressed in the CRS of the source coverage. */ private DirectPosition getSourceMedian() { if (dataGeometry.isDefined(GridGeometry.ENVELOPE)) { @@ -585,13 +586,23 @@ public class RenderingData implements Cloneable { /** * Conversion or transformation from {@linkplain PlanarCanvas#getObjectiveCRS() objective CRS} to * {@linkplain #data} CRS. This transform will include {@code WraparoundTransform} steps if needed. + * + * @param transform the transform to concatenate with a "wraparound" operation. + * @param sourceMedian point of interest in the <em>source</em> CRS of given transform. + * @param targetMedian point of interest after wraparound. + * @param targetCRS the target CRS of the given transform. */ - private static MathTransform applyWraparound(final MathTransform transform, final DirectPosition sourceMedian, + private static MathTransform applyWraparound(final MathTransform transform, DirectPosition sourceMedian, final DirectPosition targetMedian, final CoordinateReferenceSystem targetCRS) throws TransformException { if (targetMedian == null) { return transform; } + /* + * This method is invoked with `sourceMedian` expressed in the transform source CRS. + * But by contract, `WraparoundApplicator` needs that point in the transform target CRS. + */ + sourceMedian = transform.transform(sourceMedian, null); return new WraparoundApplicator(sourceMedian, targetMedian, targetCRS.getCoordinateSystem()).forDomainOfUse(transform); } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundApplicator.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundApplicator.java index e7ce027..411f3bb 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundApplicator.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundApplicator.java @@ -38,13 +38,15 @@ import org.apache.sis.measure.Units; * Each {@code WraparoundTransform} instance should be used only once. * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * @since 1.1 * @module */ public final class WraparoundApplicator { /** * Coordinates at the center of source envelope, or {@code null} if none. + * This coordinate shall be expressed in the source CRS of the {@link WraparoundTransform}, + * not in the source CRS of the concatenated transform to be created by this applicator. */ private final DirectPosition sourceMedian; @@ -60,7 +62,9 @@ public final class WraparoundApplicator { private final CoordinateSystem targetCS; /** - * Creates a new applicator. + * Creates a new applicator. The median coordinates are optional and can be null. + * Coordinates shall be in the source and target CRS of the {@link WraparoundTransform}, + * not in the source CRS of the concatenated transform to be created by this applicator. * * @param sourceMedian the coordinates at the center of source envelope, or {@code null} if none. * @param targetMedian the coordinates to put at the center of new range, or {@code null} for standard axis center. @@ -97,7 +101,7 @@ public final class WraparoundApplicator { /** * Returns the given transform augmented with a "wrap around" behavior if applicable. * The wraparound is applied on target coordinates and aims to clamp coordinate values - * in a range centered on the given median. + * in a range centered on the target median given at construction time. * * <p>The centered ranges may be different than the range declared by the coordinate system axes. * In such case, the wraparound range applied by this method will have a translation compared to @@ -105,6 +109,9 @@ public final class WraparoundApplicator { * (e.g. when transforming a raster) and we want that output coordinates to be continuous * in that domain, independently of axis ranges.</p> * + * <p>If a non-null {@code sourceMedian} position has been specified at construction time, + * those coordinates shall be expressed in the <em>target</em> CRS of given transform.</p> + * * @param tr the transform to augment with "wrap around" behavior. * @return the math transform with wraparound if needed. * @throws TransformException if a coordinate can not be computed. @@ -133,30 +140,32 @@ public final class WraparoundApplicator { if (!(period > 0 && period != Double.POSITIVE_INFINITY)) { return tr; } - double m; - if (targetMedian == null) { - final CoordinateSystemAxis axis = targetCS.getAxis(wraparoundDimension); - m = (axis.getMinimumValue() + axis.getMaximumValue()) / 2; - } else try { - m = targetMedian.getOrdinate(wraparoundDimension); + double m, sm; + try { + if (targetMedian == null) { + final CoordinateSystemAxis axis = targetCS.getAxis(wraparoundDimension); + m = (axis.getMinimumValue() + axis.getMaximumValue()) / 2; + } else { + m = targetMedian.getOrdinate(wraparoundDimension); + } + if (!Double.isFinite(m)) { + if (targetMedian != null) { + // Invalid median value. Assume caller means "no wrap". + return tr; + } + /* + * May happen if `WraparoundAdjustment.range(…)` recognized a longitude axis + * despite the `CoordinateSystemAxis` not declarining minimum/maximum values. + * Use 0 as the range center (e.g. center of [-180 … 180]° longitude range). + */ + m = 0; + } + sm = (sourceMedian != null) ? sourceMedian.getOrdinate(wraparoundDimension) : Double.NaN; } catch (BackingStoreException e) { - // Some implementations compute coordinates only when first needed. + // Some `DirectPosition` implementations compute coordinates only when first needed. throw e.unwrapOrRethrow(TransformException.class); } - if (!Double.isFinite(m)) { - if (targetMedian != null) { - // Invalid median value. Assume caller means "no wrap". - return tr; - } - /* - * May happen if `WraparoundAdjustment.range(…)` recognized a longitude axis - * despite the `CoordinateSystemAxis` not declarining minimum/maximum values. - * Use 0 as the range center (e.g. center of [-180 … 180]° longitude range). - */ - m = 0; - } - final MathTransform wraparound = WraparoundTransform.create(tr.getTargetDimensions(), wraparoundDimension, - period, (sourceMedian == null) ? Double.NaN : sourceMedian.getOrdinate(wraparoundDimension), m); + final MathTransform wraparound = WraparoundTransform.create(tr.getTargetDimensions(), wraparoundDimension, period, sm, m); return MathTransforms.concatenate(tr, wraparound); } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/package-info.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/package-info.java index f279037..b31caf3 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/package-info.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/package-info.java @@ -24,7 +24,7 @@ * may change in incompatible ways in any future version without notice. * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * @since 0.3 * @module */
