This is an automated email from the ASF dual-hosted git repository. amanin pushed a commit to branch refactor/sql-store in repository https://gitbox.apache.org/repos/asf/sis.git
commit 82901836cee6586c995d5859d75b799ebd96db2d Author: Alexis Manin <[email protected]> AuthorDate: Tue Oct 8 13:05:02 2019 +0200 wip(Feature): Add unit tests for envelope to geometry operator. --- .../apache/sis/internal/feature/Geometries.java | 20 +++--- .../sis/internal/feature/GeometriesTestCase.java | 76 ++++++++++++++++++---- 2 files changed, 74 insertions(+), 22 deletions(-) diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java index 3fd582b..0688024 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java @@ -22,6 +22,7 @@ import java.util.function.Function; import java.util.logging.Level; import java.util.logging.LogRecord; +import org.opengis.geometry.DirectPosition; import org.opengis.geometry.Envelope; import org.opengis.geometry.Geometry; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -346,7 +347,7 @@ public abstract class Geometries<G> { return Optional.empty(); } - private Object tryConvertToGeometry(final Envelope env, WrapResolution resolution) { + Object tryConvertToGeometry(final Envelope env, WrapResolution resolution) { // Ensure that we can isolate an horizontal part in the given envelope. final int x; if (env.getDimension() == 2) { @@ -361,10 +362,12 @@ public abstract class Geometries<G> { final int y = x+1; - double minX = env.getMinimum(x); - double minY = env.getMinimum(y); - double maxX = env.getMaximum(x); - double maxY = env.getMaximum(y); + final DirectPosition lc = env.getLowerCorner(); + final DirectPosition uc = env.getUpperCorner(); + double minX = lc.getOrdinate(x); + double minY = lc.getOrdinate(y); + double maxX = uc.getOrdinate(x); + double maxY = uc.getOrdinate(y); double[] splittedLeft = null; // We start by short-circuiting simplest case for minor simplicity/performance reason. if (!WrapResolution.NONE.equals(resolution)) { @@ -373,14 +376,13 @@ public abstract class Geometries<G> { fixedEnv.normalize(); int wrapAxis = -1; for (int i = x ; i <= y && wrapAxis < x ; i++) { - if (fixedEnv.getMinimum(i) > fixedEnv.getMaximum(i)) wrapAxis = i; + if (fixedEnv.getLower(i) > fixedEnv.getUpper(i)) wrapAxis = i; } if (wrapAxis >= x) { final CoordinateReferenceSystem crs = env.getCoordinateReferenceSystem(); - if (crs == null) throw new IllegalStateException("Cannot resolve wrap-around for an envelope without any system defined"); + if (crs == null) throw new IllegalArgumentException("Cannot resolve wrap-around for an envelope without any system defined"); final CoordinateSystemAxis axis = crs.getCoordinateSystem().getAxis(wrapAxis); final double wrapRange = axis.getMaximumValue() - axis.getMinimumValue(); - //TODO switch (resolution) { case EXPAND: // simpler and more performant than a call to GeneralEnvelope.simplify() @@ -422,7 +424,7 @@ public abstract class Geometries<G> { maxY = splittedLeft[3]; Vector[] points2 = clockwiseRing(minX, minY, maxX, maxY); final G secondRect = createPolyline(2, points2); - return createMultiPolygon(mainRect, secondRect); + return createMultiPolygonImpl(mainRect, secondRect); } return mainRect; diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java index 9d02c74..dc67d01 100644 --- a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java +++ b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java @@ -20,6 +20,8 @@ import java.util.Arrays; import java.util.EnumMap; import java.util.Iterator; import java.util.Map; +import java.util.concurrent.Callable; +import java.util.stream.Stream; import org.opengis.geometry.Envelope; @@ -33,9 +35,14 @@ import org.apache.sis.test.TestCase; import org.junit.Test; import static java.lang.Double.NaN; +import static org.apache.sis.internal.feature.WrapResolution.CONTIGUOUS; +import static org.apache.sis.internal.feature.WrapResolution.EXPAND; +import static org.apache.sis.internal.feature.WrapResolution.NONE; +import static org.apache.sis.internal.feature.WrapResolution.SPLIT; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; /** @@ -145,30 +152,73 @@ public abstract strictfp class GeometriesTestCase extends TestCase { @Test public void testEnvelopeToLinearRing() { + // First, ensure that behavior is constant in case wrap-around is de-activated. final GeneralEnvelope noWrapNeeded = new GeneralEnvelope(new DefaultGeographicBoundingBox(-30, 24, -60, -51)); final double[] expectedResult = {-30, -60, -30, -51, 24, -51, 24, -60, -30, -60}; for (WrapResolution method : WrapResolution.values()) assertConversion(noWrapNeeded, method, expectedResult); - final GeneralEnvelope basicWrap = new GeneralEnvelope(CommonCRS.defaultGeographic()); - basicWrap.setEnvelope(165, 32, -170, 33); + // Check behavior when wrap-around is on first axis. + final GeneralEnvelope wrapped = new GeneralEnvelope(CommonCRS.defaultGeographic()); + wrapped.setEnvelope(165, 32, -170, 33); final EnumMap expected = new EnumMap(WrapResolution.class); - expected.put(WrapResolution.NONE, new double[]{165, 32, 165, 33, -170, 33, -170, 32, 165, 32}); - expected.put(WrapResolution.CONTIGUOUS, new double[]{165, 32, 165, 33, 190, 33, 190, 32, 165, 32}); - expected.put(WrapResolution.EXPAND, new double[]{-180, 32, -180, 33, 180, 33, 180, 32, -180, 32}); - expected.put(WrapResolution.SPLIT, new double[]{165, 32, 165, 33, 180, 33, 180, 32, 165, 32, -180, 32, -180, 33, -170, 33, -170, 32, -180, 32}); - assertConversion(basicWrap, expected); - + expected.put(NONE, new double[]{165, 32, 165, 33, -170, 33, -170, 32, 165, 32}); + expected.put(CONTIGUOUS, new double[]{165, 32, 165, 33, 190, 33, 190, 32, 165, 32}); + expected.put(EXPAND, new double[]{-180, 32, -180, 33, 180, 33, 180, 32, -180, 32}); + expected.put(SPLIT, new double[]{165, 32, 165, 33, 180, 33, 180, 32, 165, 32, -180, 32, -180, 33, -170, 33, -170, 32, -180, 32}); + assertConversion(wrapped, expected); + + wrapped.setEnvelope(177, -42, 190, 2); + expected.put(NONE, new double[]{177, -42, 177, 2, 190, 2, 190, -42, 177, -42}); + expected.put(CONTIGUOUS, new double[]{177, -42, 177, 2, 190, 2, 190, -42, 177, -42}); + expected.put(EXPAND, new double[]{-180, -42, -180, 2, 180, 2, 180, -42, -180, -42}); + expected.put(SPLIT, new double[]{177, -42, 177, 2, 180, 2, 180, -42, 177, -42, -180, -42, -180, 2, -170, 2, -170, -42, -180, -42}); + + // Check wrap-around on second axis. + wrapped.setCoordinateReferenceSystem(CommonCRS.WGS84.geographic()); + wrapped.setEnvelope(2, 89, 3, 19); + expected.put(NONE, new double[]{2, 89, 2, 19, 3, 19, 3, 89, 2, 89}); + expected.put(CONTIGUOUS, new double[]{2, 89, 2, 199, 3, 199, 3, 89, 2, 89}); + expected.put(EXPAND, new double[]{2, -180, 2, 180, 3, 180, 3, -180, 2, -180}); + expected.put(SPLIT, new double[]{2, 89, 2, 180, 3, 180, 3, 89, 2, 89, 2, -180, 2, 19, 3, 19, 3, -180, 2, -180}); + + // Ensure fail fast on dimension ambiguity + wrapped.setCoordinateReferenceSystem(null); + Stream.of(CONTIGUOUS, EXPAND, SPLIT) + .forEach(method + -> expectFailFast(() + -> factory.tryConvertToGeometry(wrapped, method), IllegalArgumentException.class) + ); + + final GeneralEnvelope wrapped3d = new GeneralEnvelope(3); + expectFailFast(() -> factory.tryConvertToGeometry(wrapped3d, NONE), IllegalArgumentException.class); + } + private static void expectFailFast(Callable whichMustFail, Class<? extends Exception>... expectedErrorTypes) { + try { + final Object result = whichMustFail.call(); + fail("Fail fast expected, but successfully returned "+result); + } catch (Exception e) { + if (expectedErrorTypes == null || expectedErrorTypes.length < 1) return; // Any error accepted. + for (Class errorType : expectedErrorTypes) { + if (errorType.isInstance(e)) { + // Expected behavior + return; + } + } + // Unexpected error + fail(String.format( + "A fail fast occurred, but thrown error type is unexpected.%nError type: %s%nStack-trace: %s", + e.getClass().getCanonicalName(), e + )); + } } - private static void assertConversion(final Envelope source, final Map<WrapResolution, double[]> expectedResults) { + private void assertConversion(final Envelope source, final Map<WrapResolution, double[]> expectedResults) { expectedResults.entrySet().forEach(entry -> assertConversion(source, entry.getKey(), entry.getValue())); } - private static void assertConversion(final Envelope source, final WrapResolution method, final double[] expectedOrdinates) { - final double[] result = Geometries.toGeometry(source, method) - .flatMap(Geometries::getOrdinates) - .orElseThrow(() -> new AssertionError("No geometry returned for envelope conversion")); + private void assertConversion(final Envelope source, final WrapResolution method, final double[] expectedOrdinates) { + final double[] result = factory.getPoints(factory.tryConvertToGeometry(source, method)); assertArrayEquals("Point list for: "+method, expectedOrdinates, result, 1e-9); }
