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 ce6a4b3 Add a `GridGeometry.getEnvelope(CoordinateReferenceSystem)` method. ce6a4b3 is described below commit ce6a4b37eac2ad83e2b917cdb22aff18a5adaa6b Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Wed Jan 19 17:10:09 2022 +0100 Add a `GridGeometry.getEnvelope(CoordinateReferenceSystem)` method. --- .../org/apache/sis/coverage/grid/GridGeometry.java | 49 ++++++++++++++++++++++ .../apache/sis/coverage/grid/GridGeometryTest.java | 36 +++++++++++++++- .../java/org/apache/sis/geometry/Envelopes.java | 13 +++--- 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java index 79ae461..84b0402 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java @@ -34,6 +34,7 @@ import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.Matrix; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; +import org.opengis.referencing.operation.CoordinateOperation; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.cs.CoordinateSystem; @@ -79,6 +80,8 @@ import org.apache.sis.io.TableAppender; import org.apache.sis.xml.NilObject; import org.apache.sis.xml.NilReason; +import static org.apache.sis.referencing.CRS.findOperation; + /** * Valid extent of grid coordinates together with the transform from those grid coordinates @@ -883,6 +886,52 @@ public class GridGeometry implements LenientComparable, Serializable { } /** + * Returns the "real world" bounding box of this grid geometry transformed to the given CRS. + * This envelope is computed from the {@linkplain #getExtent() grid extent} if available, + * or from the {@linkplain #getEnvelope() envelope} otherwise. + * + * @param crs the desired coordinate reference system for the returned envelope. + * @return the bounding box in "real world" coordinates (never {@code null}). + * @throws IncompleteGridGeometryException if this grid geometry has no extent and no envelope. + * @throws TransformException if the envelope can not be transformed to the specified CRS. + * + * @since 1.2 + */ + public Envelope getEnvelope(final CoordinateReferenceSystem crs) throws TransformException { + ArgumentChecks.ensureNonNull("crs", crs); + final int bitmask; // CRS, EXTENT or GRID_TO_CRS + final short errorKey; // Resource key for error message. + final CoordinateReferenceSystem sourceCRS = getCoordinateReferenceSystem(envelope); + if (Utilities.equalsIgnoreMetadata(sourceCRS, crs)) { + return envelope; + } else if (sourceCRS == null) { + bitmask = CRS; + errorKey = Resources.Keys.UnspecifiedCRS; + } else if (extent == null && envelope == null) { + bitmask = EXTENT; + errorKey = Resources.Keys.UnspecifiedGridExtent; + } else if (cornerToCRS == null && envelope == null) { + bitmask = GRID_TO_CRS; + errorKey = Resources.Keys.UnspecifiedTransform; + } else try { + final CoordinateOperation op = findOperation(sourceCRS, crs, geographicBBox()); + final Envelope clip = (envelope != null) ? Envelopes.transform(op, envelope) : null; + if (extent == null || cornerToCRS == null) { + return clip; + } + MathTransform tr = MathTransforms.concatenate(cornerToCRS, op.getMathTransform()); + final GeneralEnvelope env = extent.toCRS(tr, tr, clip); + env.setCoordinateReferenceSystem(op.getTargetCRS()); + env.normalize(); + env.intersect(clip); + return env; + } catch (FactoryException e) { + throw new TransformException(e); + } + throw incomplete(bitmask, errorKey); + } + + /** * Returns the approximate latitude and longitude coordinates of the grid. * The prime meridian is Greenwich, but the geodetic reference frame is not necessarily WGS 84. * This is computed from the {@linkplain #getEnvelope() envelope} if the coordinate reference system diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java index b5929b9..c13bac0 100644 --- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java +++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java @@ -29,6 +29,7 @@ import org.apache.sis.referencing.operation.matrix.Matrix4; 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.HardCodedConversions; import org.apache.sis.referencing.crs.HardCodedCRS; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.test.DependsOn; @@ -42,7 +43,7 @@ import static org.apache.sis.test.ReferencingAssert.*; * Tests the {@link GridGeometry} implementation. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.1 + * @version 1.2 * @since 1.0 * @module */ @@ -433,6 +434,39 @@ public final strictfp class GridGeometryTest extends TestCase { } /** + * Tests {@link GridGeometry#getEnvelope(CoordinateReferenceSystem)}. + * + * @throws TransformException if coordinates can not be transformed. + */ + @Test + public void testGetEnvelope() throws TransformException { + GridGeometry grid = new GridGeometry( + new GridExtent(12, 18), + PixelInCell.CELL_CORNER, + MathTransforms.linear(new Matrix3( + 0.25, 0, -2, + 0, -0.25, -3, + 0, 0, 1)), + HardCodedCRS.WGS84); + + Envelope envelope = grid.getEnvelope(HardCodedCRS.WGS84); + assertSame(envelope, grid.getEnvelope()); + assertEnvelopeEquals(new GeneralEnvelope( + new double[] {-2, -7.5}, + new double[] { 1, -3.0}), envelope, STRICT); + + envelope = grid.getEnvelope(HardCodedCRS.WGS84_LATITUDE_FIRST); + assertEnvelopeEquals(new GeneralEnvelope( + new double[] {-7.5, -2}, + new double[] {-3.0, 1}), envelope, STRICT); + + envelope = grid.getEnvelope(HardCodedConversions.mercator()); + assertEnvelopeEquals(new GeneralEnvelope( + new double[] {-222638.98, -831717.36}, + new double[] { 111319.49, -331876.53}), envelope, 0.01); + } + + /** * Tests {@link GridGeometry#translate(long...)}. */ @Test diff --git a/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java b/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java index 5d318bb..9a44839 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelopes.java @@ -48,6 +48,7 @@ import org.apache.sis.internal.referencing.CoordinateOperations; import org.apache.sis.internal.referencing.DirectPositionView; import org.apache.sis.internal.referencing.TemporalAccessor; import org.apache.sis.internal.system.Loggers; +import org.apache.sis.internal.util.Numerics; import org.apache.sis.util.logging.Logging; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.ArgumentChecks; @@ -758,7 +759,7 @@ nextPoint: for (int pointIndex = 0;;) { // Break condition at th long isWrapAroundAxis = 0; final int dimension = targetCS.getDimension(); poles: for (int i=0; i<dimension; i++) { - final long dimensionBitMask = 1L << i; + final long dimensionBitMask = Numerics.bitmask(i); final CoordinateSystemAxis axis = targetCS.getAxis(i); if (axis == null) { // Should never be null, but check as a paranoiac safety. continue; @@ -857,7 +858,7 @@ poles: for (int i=0; i<dimension; i++) { if (includedBoundsValue != 0) { while (isWrapAroundAxis != 0) { final int wrapAroundDimension = Long.numberOfTrailingZeros(isWrapAroundAxis); - final long dimensionBitMask = 1L << wrapAroundDimension; + final long dimensionBitMask = Numerics.bitmask(wrapAroundDimension); isWrapAroundAxis &= ~dimensionBitMask; // Clear now the bit, for the next iteration. final CoordinateSystemAxis wrapAroundAxis = targetCS.getAxis(wrapAroundDimension); final double min = wrapAroundAxis.getMinimumValue(); @@ -937,7 +938,7 @@ poles: for (int i=0; i<dimension; i++) { * the [-180 … +180]° range and the target CRS uses the [0 … 360]° range, or the converse. We do not wrap * around if the source and target axes use the same range (e.g. the longitude stay [-180 … +180]°) in order * to reduce the risk of discontinuities. If the user really wants unconditional wrap around, (s)he can call - * GeneralEnvelope.normalize(). + * `GeneralEnvelope.normalize()`. */ final Set<Integer> wrapAroundChanges; if (isOperationComplete && operation instanceof AbstractCoordinateOperation) { @@ -958,16 +959,16 @@ poles: for (int i=0; i<dimension; i++) { * that every points in a {@code LINESTRING} have the same dimension. However this method * ensures that the parenthesis are balanced, in order to catch some malformed WKT. * - * <p>Example:</p> + * <div class="note"><b>Examples:</b> * <ul> * <li>{@code BOX(-180 -90, 180 90)} (not really a geometry, but understood by many software products)</li> * <li>{@code POINT(6 10)}</li> * <li>{@code MULTIPOLYGON(((1 1, 5 1, 1 5, 1 1),(2 2, 3 2, 3 3, 2 2)))}</li> * <li>{@code GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(3 8,7 10))}</li> * </ul> + * </div> * - * See {@link GeneralEnvelope#GeneralEnvelope(CharSequence)} for more information about the - * parsing rules. + * See {@link GeneralEnvelope#GeneralEnvelope(CharSequence)} for more information about the parsing rules. * * @param wkt the {@code BOX}, {@code POLYGON} or other kind of element to parse. * @return the envelope of the given geometry.