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 dd658ab6ac026e6d48e30935aa22e46154903384 Author: Alexis Manin <[email protected]> AuthorDate: Tue Oct 8 15:29:35 2019 +0200 feat(Feature): ends Envelope to geometry feature --- .../java/org/apache/sis/internal/feature/ESRI.java | 39 +++++--- .../apache/sis/internal/feature/Geometries.java | 15 +++ .../org/apache/sis/internal/feature/Java2D.java | 105 ++++++++++++++++++++- .../sis/internal/feature/GeometriesTestCase.java | 3 +- 4 files changed, 143 insertions(+), 19 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 05be9ff..ce05495 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 @@ -23,18 +23,9 @@ import org.apache.sis.math.Vector; import org.apache.sis.setup.GeometryLibrary; import org.apache.sis.util.Classes; -import com.esri.core.geometry.Envelope2D; -import com.esri.core.geometry.Geometry; -import com.esri.core.geometry.MultiPath; -import com.esri.core.geometry.OperatorExportToWkt; -import com.esri.core.geometry.OperatorImportFromWkt; -import com.esri.core.geometry.Point; -import com.esri.core.geometry.Point2D; -import com.esri.core.geometry.Point3D; -import com.esri.core.geometry.Polygon; -import com.esri.core.geometry.Polyline; -import com.esri.core.geometry.WktExportFlags; -import com.esri.core.geometry.WktImportFlags; +import com.esri.core.geometry.*; + +import static org.apache.sis.util.ArgumentChecks.ensureNonNull; /** @@ -198,12 +189,32 @@ add: for (;;) { @Override double[] getPoints(Object geometry) { - throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)" on 07/10/2019 + if (geometry instanceof GeometryWrapper) geometry = ((GeometryWrapper) geometry).geometry; + ensureNonNull("Geometry", geometry); + if (geometry instanceof MultiVertexGeometry) { + MultiVertexGeometry vertices = (MultiVertexGeometry) geometry; + final Point2D[] coords = vertices.getCoordinates2D(); + final double[] ordinates = new double[coords.length*2]; + int idx = 0; + for (Point2D coord : coords) { + ordinates[idx++] = coord.x; + ordinates[idx++] = coord.y; + } + return ordinates; + } + throw new UnsupportedOperationException("Unsupported geometry type: "+geometry.getClass().getCanonicalName()); } @Override Object createMultiPolygonImpl(Object... polygonsOrLinearRings) { - throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)" on 07/10/2019 + final Polygon poly = new Polygon(); + for (final Object polr : polygonsOrLinearRings) { + if (polr instanceof MultiPath) { + poly.add((MultiPath) polr, false); + } else throw new UnsupportedOperationException("Unsupported geometry type: "+polr == null? "null" : polr.getClass().getCanonicalName()); + } + + return poly; } /** 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 0688024..31094c9 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 @@ -157,6 +157,14 @@ public abstract class Geometries<G> { return false; } + /** + * Transforms an envelope to a polygon whose start point is lower corner, and points composing result are the + * envelope corners in clockwise order. + * @param env The envelope to convert. + * @param wraparound How to resolve wrap-around ambiguities on the envelope. + * @return If any geometric implementation is installed, return a polygon (or two polygons in case of + * {@link WrapResolution#SPLIT split handling of wrap-around}. + */ public static Optional<Geometry> toGeometry(final Envelope env, WrapResolution wraparound) { return findStrategy(g -> g.tryConvertToGeometry(env, wraparound)) .map(result -> new GeometryWrapper(result, env)); @@ -347,6 +355,9 @@ public abstract class Geometries<G> { return Optional.empty(); } + /** + * See {@link Geometries#toGeometry(Envelope, WrapResolution)}. + */ Object tryConvertToGeometry(final Envelope env, WrapResolution resolution) { // Ensure that we can isolate an horizontal part in the given envelope. final int x; @@ -427,6 +438,10 @@ public abstract class Geometries<G> { return createMultiPolygonImpl(mainRect, secondRect); } + /* Geotk original method had an option to insert a median point on wrappped around axis, but we have not ported + * it, because in an orthonormal space, I don't see any case where it could be useful. However, in case it + * have to be added, we can do it here by amending created ring(s). + */ return mainRect; } 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 58c7b29..38f78d8 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 @@ -16,20 +16,30 @@ */ package org.apache.sis.internal.feature; -import java.util.Iterator; import java.awt.Shape; import java.awt.geom.Line2D; import java.awt.geom.Path2D; +import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.stream.DoubleStream; +import java.util.stream.StreamSupport; + import org.apache.sis.geometry.GeneralEnvelope; -import org.apache.sis.setup.GeometryLibrary; import org.apache.sis.internal.feature.j2d.ShapeProperties; import org.apache.sis.internal.referencing.j2d.ShapeUtilities; import org.apache.sis.math.Vector; +import org.apache.sis.setup.GeometryLibrary; import org.apache.sis.util.Classes; import org.apache.sis.util.Numbers; +import static org.apache.sis.util.ArgumentChecks.ensureNonEmpty; +import static org.apache.sis.util.ArgumentChecks.ensureNonNull; + /** * Centralizes usages of some (not all) Java2D geometry API by Apache SIS. @@ -221,12 +231,25 @@ add: for (;;) { @Override double[] getPoints(Object geometry) { - throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)" on 07/10/2019 + if (geometry instanceof GeometryWrapper) geometry = ((GeometryWrapper) geometry).geometry; + ensureNonNull("Geometry", geometry); + if (geometry instanceof Point2D) return getCoordinate(geometry); + if (geometry instanceof Shape) { + final PathIterator it = ((Shape) geometry).getPathIterator(null); + return StreamSupport.stream(new PathSpliterator(it), false) + .flatMapToDouble(Segment::ordinates) + .toArray(); + } + + throw new UnsupportedOperationException("Unsupported geometry type: "+geometry.getClass().getCanonicalName()); } @Override Object createMultiPolygonImpl(Object... polygonsOrLinearRings) { - throw new UnsupportedOperationException("Not supported yet"); // "Alexis Manin (Geomatys)" on 07/10/2019 + ensureNonEmpty("Polygons or linear rings to merge", polygonsOrLinearRings); + if (polygonsOrLinearRings.length == 1) return polygonsOrLinearRings[0]; + final Iterator<Object> it = Arrays.asList(polygonsOrLinearRings).iterator(); + return tryMergePolylines(it.next(), it); } /** @@ -248,4 +271,78 @@ add: for (;;) { public Object parseWKT(final String wkt) { throw unsupported(2); } + + /** + * An abstraction over {@link PathIterator} to use it in a streaming context. + */ + private static class PathSpliterator implements Spliterator<Segment> { + + private final PathIterator source; + + private PathSpliterator(PathIterator source) { + this.source = source; + } + + @Override + public boolean tryAdvance(Consumer<? super Segment> action) { + if (source.isDone()) return false; + final double[] coords = new double[6]; + final int segmentType = source.currentSegment(coords); + action.accept(new Segment(segmentType, coords)); + source.next(); + return true; + } + + @Override + public Spliterator<Segment> trySplit() { + return null; + } + + @Override + public long estimateSize() { + return Long.MAX_VALUE; + } + + @Override + public int characteristics() { + return Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.IMMUTABLE; + } + } + + /** + * Describe a path segment as described by {@link PathIterator#currentSegment(double[]) AWT path iteration API}. + * Basically, the awt abstraction is really poor, so we made a wrapper around it to describe segments. + */ + private static class Segment { + /** + * This segment type ({@link PathIterator#SEG_CLOSE}, etc.). + */ + final int type; + /** + * Brut points composing the segment, as returned by {@link PathIterator#currentSegment(double[])}. + */ + private final double[] points; + + Segment(int type, double[] points) { + this.type = type; + this.points = points; + } + + /** + * + * @return points composing this segment, as a contiguous set of ordinates. Points are all 2D, so every two + * elements in backed stream describe a point. Can be empty in case of {@link PathIterator#SEG_CLOSE closing segment}. + */ + DoubleStream ordinates() { + switch (type) { + case PathIterator.SEG_CLOSE: return DoubleStream.empty(); + case PathIterator.SEG_QUADTO: return Arrays.stream(points, 0, 4); + case PathIterator.SEG_CUBICTO: return Arrays.stream(points); + case PathIterator.SEG_LINETO: + case PathIterator.SEG_MOVETO: + return Arrays.stream(points, 0, 2); + default: throw new IllegalStateException("Unknown segment type: "+type); + } + } + } } 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 dc67d01..7aa7a44 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 @@ -186,7 +186,8 @@ public abstract strictfp class GeometriesTestCase extends TestCase { Stream.of(CONTIGUOUS, EXPAND, SPLIT) .forEach(method -> expectFailFast(() - -> factory.tryConvertToGeometry(wrapped, method), IllegalArgumentException.class) + -> factory.tryConvertToGeometry(wrapped, method) + , IllegalArgumentException.class) ); final GeneralEnvelope wrapped3d = new GeneralEnvelope(3);
