sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ama...@apache.org
Subject [sis] 26/45: wip(Feature): envelope to geometry
Date Tue, 12 Nov 2019 16:44:53 GMT
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 e39706a9de2075642d46fd69c9692d452e1128b5
Author: Alexis Manin <amanin@apache.org>
AuthorDate: Mon Oct 7 18:16:03 2019 +0200

    wip(Feature): envelope to geometry
---
 .../java/org/apache/sis/internal/feature/ESRI.java |  17 ++-
 .../apache/sis/internal/feature/Geometries.java    | 153 +++++++++++++++------
 .../java/org/apache/sis/internal/feature/JTS.java  |  44 +++++-
 .../org/apache/sis/internal/feature/Java2D.java    |  10 ++
 .../sis/internal/feature/GeometriesTestCase.java   |  43 +++++-
 5 files changed, 208 insertions(+), 59 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java
index 601b6b7..05be9ff 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/ESRI.java
@@ -18,8 +18,6 @@ package org.apache.sis.internal.feature;
 
 import java.util.Iterator;
 
-import org.opengis.geometry.Envelope;
-
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.math.Vector;
 import org.apache.sis.setup.GeometryLibrary;
@@ -88,11 +86,6 @@ final class ESRI extends Geometries<Geometry> {
         return null;
     }
 
-    @Override
-    Object tryConvertToGeometry(Envelope env) {
-        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 03/10/2019
-    }
-
     /**
      * If the given point is an implementation of this library, returns its coordinate.
      * Otherwise returns {@code null}. If non-null, the returned array may have a length
of 2 or 3.
@@ -203,6 +196,16 @@ add:    for (;;) {
         return path;
     }
 
+    @Override
+    double[] getPoints(Object geometry) {
+        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 07/10/2019
+    }
+
+    @Override
+    Object createMultiPolygonImpl(Object... polygonsOrLinearRings) {
+        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 07/10/2019
+    }
+
     /**
      * Parses the given WKT.
      */
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 8f4fcfb..3fd582b 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
@@ -24,10 +24,15 @@ import java.util.logging.LogRecord;
 
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.Geometry;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.crs.SingleCRS;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
 
 import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.internal.referencing.AxisDirections;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.math.Vector;
+import org.apache.sis.referencing.CRS;
 import org.apache.sis.setup.GeometryLibrary;
 import org.apache.sis.util.collection.BackingStoreException;
 import org.apache.sis.util.logging.Logging;
@@ -156,8 +161,6 @@ public abstract class Geometries<G> {
                 .map(result -> new GeometryWrapper(result, env));
     }
 
-    abstract Object tryConvertToGeometry(final Envelope env, WrapResolution wraparound);
-
     /**
      * If the given point is an implementation of this library, returns its coordinate.
      * Otherwise returns {@code null}.
@@ -343,60 +346,124 @@ public abstract class Geometries<G> {
         return Optional.empty();
     }
 
-    private Object envelope2Polygon(final Envelope env, WrapResolution resolution) {
-        double[] ordinates;
-        double[] secondEnvelopeIfSplit = null;
-        if (WrapResolution.NONE.equals(resolution)) {
-            ordinates = new double[] {
-                    env.getMinimum(0),
-                    env.getMinimum(1),
-                    env.getMaximum(0),
-                    env.getMaximum(1)
-            };
+    private 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) {
+            x = 0;
         } else {
-            final boolean xWrap = env.getMinimum(0) > env.getMaximum(0);
-            final boolean yWrap = env.getMinimum(1) > env.getMaximum(1);
-
-            //TODO
-            switch (resolution) {
-                case EXPAND:
-                case SPLIT:
-                case CONTIGUOUS:
-                default: throw new IllegalArgumentException("Unknown or unset wrap resolution:
"+resolution);
+            final CoordinateReferenceSystem crs = env.getCoordinateReferenceSystem();
+            if (crs == null) throw new IllegalArgumentException("Envelope with more than
2 dimensions, but without CRS: cannot isolate horizontal part.");
+            final SingleCRS hCrs = CRS.getHorizontalComponent(crs);
+            if (hCrs == null) throw new IllegalArgumentException("Cannot find an horizontal
part in given CRS");
+            x = AxisDirections.indexOfColinear(crs.getCoordinateSystem(), hCrs.getCoordinateSystem());
+        }
+
+        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);
+        double[] splittedLeft = null;
+        // We start by short-circuiting simplest case for minor simplicity/performance reason.
+        if (!WrapResolution.NONE.equals(resolution)) {
+            // ensure the envelope is correctly defined, by forcing non-authorized wrapped
axes to take entire crs span.
+            final GeneralEnvelope fixedEnv = new GeneralEnvelope(env);
+            fixedEnv.normalize();
+            int wrapAxis = -1;
+            for (int i = x ; i <= y && wrapAxis < x ; i++) {
+                if (fixedEnv.getMinimum(i) > fixedEnv.getMaximum(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");
+                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()
+                        if (wrapAxis == x) {
+                            minX = axis.getMinimumValue();
+                            maxX = axis.getMaximumValue();
+                        } else {
+                            minY = axis.getMinimumValue();
+                            maxY = axis.getMaximumValue();
+                        }
+                        break;
+                    case SPLIT:
+                        if (wrapAxis == x) {
+                            splittedLeft = new double[]{axis.getMinimumValue(), minY, maxX,
maxY};
+                            maxX = axis.getMaximumValue();
+                        }
+                        else {
+                            splittedLeft = new double[] {minX, axis.getMinimumValue(), maxX,
maxY};
+                            maxY = axis.getMaximumValue();
+                        }
+                        break;
+                    case CONTIGUOUS:
+                        if (wrapAxis == x) maxX += wrapRange;
+                        else maxY += wrapRange;
+                        break;
+                    default:
+                        throw new IllegalArgumentException("Unknown or unset wrap resolution:
" + resolution);
+                }
             }
+        }
+
+        Vector[] points = clockwiseRing(minX, minY, maxX, maxY);
 
+        final G mainRect = createPolyline(2, points);
+        if (splittedLeft != null) {
+            minX = splittedLeft[0];
+            minY = splittedLeft[1];
+            maxX = splittedLeft[2];
+            maxY = splittedLeft[3];
+            Vector[] points2 = clockwiseRing(minX, minY, maxX, maxY);
+            final G secondRect = createPolyline(2, points2);
+            return createMultiPolygon(mainRect, secondRect);
         }
 
+        return mainRect;
+    }
 
-        double minX = ordinates[0];
-        double minY = ordinates[1];
-        double maxX = ordinates[2];
-        double maxY = ordinates[3];
-        Vector[] points = {
+    /**
+     * Create a sequence of points describing a rectangle whose start point is the lower
left one. The sequence of
+     * points describe each corner, going in clockwise order and repeating starting point
to properly close the ring.
+     *
+     * @param minX Lower coordinate of first axis.
+     * @param minY Lower coordinate of second axis.
+     * @param maxX Upper coordinate of first axis.
+     * @param maxY Upper coordinate of second axis.
+     *
+     * @return A set of 5 points describing given rectangle.
+     */
+    private static Vector[] clockwiseRing(final double minX, final double minY, final double
maxX, final double maxY) {
+        return new Vector[]{
                 Vector.create(new double[]{minX, minY}),
                 Vector.create(new double[]{minX, maxY}),
                 Vector.create(new double[]{maxX, maxY}),
                 Vector.create(new double[]{maxX, minY}),
                 Vector.create(new double[]{minX, minY})
         };
+    }
 
-        final G mainRect = createPolyline(2, points);
-        if (secondEnvelopeIfSplit != null) {
-            minX = secondEnvelopeIfSplit[0];
-            minY = secondEnvelopeIfSplit[1];
-            maxX = secondEnvelopeIfSplit[2];
-            maxY = secondEnvelopeIfSplit[3];
-            Vector[] points2 = {
-                    Vector.create(new double[]{minX, minY}),
-                    Vector.create(new double[]{minX, maxY}),
-                    Vector.create(new double[]{maxX, maxY}),
-                    Vector.create(new double[]{maxX, minY}),
-                    Vector.create(new double[]{minX, minY})
-            };
-            final G secondRect = createPolyline(2, points2);
-            // TODO: merge then send back
-        }
+    /**
+     * Extract all points from input geometry, and return them as a contiguous set of ordinates.
+     * For rings, point order (clockwise/counter-clockwise) is implementation dependant.
+     *
+     * @param geometry The geometry to extract point from.
+     */
+    public static Optional<double[]> getOrdinates(Geometry geometry) {
+        return findStrategy(g -> g.getPoints(geometry));
+    }
 
-        return mainRect;
+    abstract double[] getPoints(Object geometry);
+
+    abstract Object createMultiPolygonImpl(final Object... polygonsOrLinearRings);
+
+    public static Object createMultiPolygon(final Object... polygonsOrLinearRings) {
+        return findStrategy(g -> g.createMultiPolygonImpl(polygonsOrLinearRings));
     }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
index 034bd65..90fa183 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
@@ -31,6 +31,7 @@ import org.locationtech.jts.geom.Envelope;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.LinearRing;
 import org.locationtech.jts.geom.MultiLineString;
 import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.geom.Polygon;
@@ -113,13 +114,6 @@ final class JTS extends Geometries<Geometry> {
         return null;
     }
 
-    @Override
-    Geometry tryConvertToGeometry(org.opengis.geometry.Envelope env, final WrapResolution
resolution) {
-        final int dim = env.getDimension();
-        if (dim > 2) throw new UnsupportedOperationException("Cannot manage more than
2 dimensions, but input envelope has "+dim);
-        throw new UnsupportedOperationException("Not yet");
-    }
-
     /**
      * If the given point is an implementation of this library, returns its coordinate.
      * Otherwise returns {@code null}. If non-null, the returned array may have a length
of 2 or 3.
@@ -273,4 +267,40 @@ add:    for (;;) {
         toLineString(coordinates, lines);
         return toGeometry(lines);
     }
+
+    @Override
+    double[] getPoints(Object geometry) {
+        if (geometry instanceof GeometryWrapper) {
+            geometry = ((GeometryWrapper) geometry).geometry;
+        }
+
+        if (geometry instanceof Geometry) {
+            Geometry geom = (Geometry) geometry;
+            final int nbPts = geom.getNumPoints();
+            final Coordinate[] coords = geom.getCoordinates();
+            double[] ordinates = new double[nbPts * 2];
+            for (int i = 0, j=0; i < nbPts ; i++) {
+                final Coordinate coord = coords[i];
+                ordinates[j++] = coord.x;
+                ordinates[j++] = coord.y;
+            }
+            return ordinates;
+        }
+
+        return null;
+    }
+
+    @Override
+    Object createMultiPolygonImpl(Object... polygonsOrLinearRings) {
+        final Polygon[] polys = new Polygon[polygonsOrLinearRings.length];
+        for (int i = 0 ; i < polys.length ; i++) {
+            Object o = polygonsOrLinearRings[i];
+            if (o instanceof GeometryWrapper) o = ((GeometryWrapper) o).geometry;
+
+            if (o instanceof Polygon) polys[i] = (Polygon) o;
+            else if (o instanceof LinearRing) polys[i] = factory.createPolygon((LinearRing)
o);
+        }
+
+        return factory.createMultiPolygon(polys);
+    }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java
index f0a70f3..58c7b29 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Java2D.java
@@ -219,6 +219,16 @@ add:    for (;;) {
         return ShapeUtilities.toPrimitive(path);
     }
 
+    @Override
+    double[] getPoints(Object geometry) {
+        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 07/10/2019
+    }
+
+    @Override
+    Object createMultiPolygonImpl(Object... polygonsOrLinearRings) {
+        throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)"
on 07/10/2019
+    }
+
     /**
      * If the given object is a Java2D shape, builds its WKT representation.
      * Current implementation assumes that all closed shapes are polygons and that polygons
have no hole
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 cbef788..9d02c74 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
@@ -17,15 +17,25 @@
 package org.apache.sis.internal.feature;
 
 import java.util.Arrays;
+import java.util.EnumMap;
 import java.util.Iterator;
-import org.apache.sis.math.Vector;
+import java.util.Map;
+
+import org.opengis.geometry.Envelope;
+
 import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.math.Vector;
+import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
+import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.TestCase;
+
 import org.junit.Test;
 
 import static java.lang.Double.NaN;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 
 
 /**
@@ -133,6 +143,35 @@ public abstract strictfp class GeometriesTestCase extends TestCase {
         assertWktEquals("LINESTRING (4 5, 7 9, 9 3, -1 -6)", text);
     }
 
+    @Test
+    public void testEnvelopeToLinearRing() {
+        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);
+        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);
+
+
+    }
+
+    private static 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"));
+        assertArrayEquals("Point list for: "+method, expectedOrdinates, result, 1e-9);
+    }
+
     /**
      * Verifies that a WKT is equal to the expected one. If the actual WKT is a multi-lines
or multi-polygons,
      * then this method may modify the expected WKT accordingly. This adjustment is done
for the ESRI case by


Mime
View raw message