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 d41d7fa8386e0d9e196fe92e5bbbd5c8dbac0041 Author: Martin Desruisseaux <[email protected]> AuthorDate: Sat Jul 2 17:36:42 2022 +0200 Make a better use of new `MathTransforms.getDomain(…)` API in JavaFX viewer. --- .../sis/internal/map/coverage/RenderingData.java | 67 ++++------------------ .../java/org/apache/sis/geometry/Shapes2D.java | 22 ++++++- 2 files changed, 31 insertions(+), 58 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 bb24a390cb..f87b995e2e 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 @@ -30,7 +30,6 @@ import java.awt.geom.Rectangle2D; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import org.opengis.util.FactoryException; -import org.opengis.geometry.Envelope; import org.opengis.geometry.DirectPosition; import org.opengis.metadata.extent.GeographicBoundingBox; import org.opengis.referencing.datum.PixelInCell; @@ -48,7 +47,6 @@ import org.apache.sis.coverage.grid.PixelTranslation; import org.apache.sis.coverage.SampleDimension; import org.apache.sis.geometry.AbstractEnvelope; import org.apache.sis.geometry.Envelope2D; -import org.apache.sis.geometry.Envelopes; import org.apache.sis.geometry.Shapes2D; import org.apache.sis.image.PlanarImage; import org.apache.sis.image.ErrorHandler; @@ -185,21 +183,6 @@ public class RenderingData implements Cloneable { */ private GridGeometry dataGeometry; - /** - * The domain of validity of the objective CRS in units of the {@link #data} image. - * This value needs to be recomputed when the objective CRS or the {@link #dataGeometry} changes. - * It is used for avoiding failure to project a part of the image too far from what the projection - * can handle, for example polar areas with a Mercator projection. - */ - private Rectangle domainOfValidity; - - /** - * A value for {@link #domainOfValidity} meaning that there is no limits. Should not be modified. - */ - private static final Rectangle NO_LIMITS = new Rectangle( - Integer.MIN_VALUE/2, Integer.MIN_VALUE/2, - Integer.MAX_VALUE, Integer.MAX_VALUE); - /** * Ranges of sample values in each band of {@link #data}. This is used for determining on which sample values * to apply colors when user asked to apply a color ramp. May be {@code null}. @@ -289,7 +272,6 @@ public class RenderingData implements Cloneable { changeOfCRS = null; cornerToObjective = null; objectiveToCenter = null; - domainOfValidity = null; } /** @@ -439,7 +421,6 @@ public class RenderingData implements Cloneable { final MathTransform inverse = concatenate(PixelInCell.CELL_CENTER, old, dataGeometry, toNew); cornerToObjective = MathTransforms.concatenate(forward, cornerToObjective); objectiveToCenter = MathTransforms.concatenate(objectiveToCenter, inverse); - domainOfValidity = null; // Will be recomputed when needed. } return true; } @@ -568,40 +549,6 @@ public class RenderingData implements Cloneable { } } - /** - * Returns the bounds of the given image clipped to the domain of validity of the objective CRS. - * The intent is to avoid a failure to reproject the image, for example if the image reaches the - * poles and the objective CRS is a Mercator projection. - */ - private Rectangle getValidImageBounds(final RenderedImage image) throws TransformException { - Rectangle bounds = ImageUtilities.getBounds(image); - if (domainOfValidity == NO_LIMITS) { // Identity comparison is okay here (quick check). - return bounds; - } - if (domainOfValidity == null) { - Envelope domain = MathTransforms.getDomain(changeOfCRS.getMathTransform().inverse()).orElse(null); - if (domain == null) { - domainOfValidity = NO_LIMITS; - return bounds; - } - domain = Envelopes.transform(cornerToObjective.inverse(), domain); - double x = domain.getMinimum(0); - double y = domain.getMinimum(1); - double w = domain.getSpan(0); - double h = domain.getSpan(1); - if (!(x >= Integer.MIN_VALUE)) x = Integer.MIN_VALUE/2; // Use `!` for catching NaN. - if (!(y >= Integer.MIN_VALUE)) y = Integer.MIN_VALUE/2; - if (!(h <= Integer.MAX_VALUE)) h = Integer.MAX_VALUE; - if (!(w <= Integer.MAX_VALUE)) w = Integer.MAX_VALUE; - domainOfValidity = new Rectangle( - (int) Math.min(x, Integer.MAX_VALUE), - (int) Math.min(y, Integer.MAX_VALUE), - (int) Math.max(w, 0), - (int) Math.max(h, 0)); - } - return bounds.intersection(domainOfValidity); - } - /** * Creates the resampled image, then optionally applies an index color model. * This method will compute the {@link MathTransform} steps from image coordinate system @@ -650,7 +597,7 @@ public class RenderingData implements Cloneable { /* * Create a resampled image for current zoom level. If the image is zoomed, the resampled image bounds * will be very large, potentially larger than 32 bit integer capacity (calculation done below clamps - * the result to 32 bit integer range). This is okay since only visible tiles will be created. + * the result to 32 bit integer range). This is okay because only visible tiles will be created. * * NOTE: if user pans image close to integer range limit, a new resampled image will need to be computed * for shifting away from integer overflow risk situation. This check is done by the caller. @@ -659,9 +606,15 @@ public class RenderingData implements Cloneable { displayToObjective = AffineTransforms2D.castOrCopy(inverse); MathTransform cornerToDisplay = MathTransforms.concatenate(cornerToObjective, objectiveToDisplay); MathTransform displayToCenter = MathTransforms.concatenate(inverse, objectiveToCenter); - final Rectangle bounds = (Rectangle) Shapes2D.transform( - MathTransforms.bidimensional(cornerToDisplay), - getValidImageBounds(recoloredImage), new Rectangle()); + /* + * If the source image is world-wide and if the transform involves a projection that can not represent + * the whole world, then we need to clip the image to a domain supported by the map projection. + */ + final Rectangle bounds = ImageUtilities.getBounds(recoloredImage); + MathTransforms.getDomain(cornerToDisplay).ifPresent((domain) -> { + Shapes2D.intersect(bounds, domain, 0, 1); + }); + Shapes2D.transform(MathTransforms.bidimensional(cornerToDisplay), bounds, bounds); /* * Verify if wraparound is really necessary. We do this check because the `displayToCenter` transform * may be used for every pixels, so it is worth to make that transform more efficient if possible. diff --git a/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java b/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java index ed6d594bf1..31b37d4f20 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/geometry/Shapes2D.java @@ -22,6 +22,8 @@ import java.awt.geom.Line2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import java.awt.geom.AffineTransform; +import java.awt.geom.RectangularShape; +import org.opengis.geometry.Envelope; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -49,7 +51,7 @@ import org.apache.sis.util.Static; * * @author Martin Desruisseaux (IRD, Geomatys) * @author Johann Sorel (Geomatys) - * @version 1.1 + * @version 1.3 * @since 0.8 * @module */ @@ -148,6 +150,24 @@ public final class Shapes2D extends Static { 2*radius, 2*radius); } + /** + * Sets the given shape to the intersection of that shape with the given envelope. + * + * @param shape the shape to intersect. Will be modified in-place. + * @param envelope the envelope to intersect with the given shape. + * @param xdim dimension of <var>x</var> coordinates in the envelope. This is usually 0. + * @param ydim dimension of <var>y</var> coordinates in the envelope. This is usually 1. + * + * @since 1.3 + */ + public static void intersect(final RectangularShape shape, final Envelope envelope, final int xdim, final int ydim) { + final double xmin = Math.max(shape.getMinX(), envelope.getMinimum(xdim)); + final double ymin = Math.max(shape.getMinY(), envelope.getMinimum(ydim)); + final double xmax = Math.min(shape.getMaxX(), envelope.getMaximum(xdim)); + final double ymax = Math.min(shape.getMaxY(), envelope.getMaximum(ydim)); + shape.setFrame(xmin, ymin, xmax - xmin, ymax - ymin); + } + /** * Transforms a rectangular envelope using the given math transform. * The transformation is only approximated: the returned envelope may be bigger than
