From 47f040c8baf2b7de3472c483bb49e814acf29532 Mon Sep 17 00:00:00 2001 From: Danylo Kravchenko Date: Thu, 2 Nov 2023 20:07:57 +0100 Subject: [PATCH] GIS #462: added Polygons and test for them --- .../db/type/entity/spatial/PolyGeometry.java | 176 +++++++++++++++--- .../type/entity/spatial/PolyGeometryType.java | 53 +++++- .../type/entity/spatial/PolyLineString.java | 11 +- .../type/entity/spatial/PolyLinearRing.java | 4 +- .../db/type/entity/spatial/PolyPoint.java | 12 ++ .../db/type/entity/spatial/PolyPolygon.java | 89 +++++++++ .../db/type/spatial/GeometryConstants.java | 1 + .../db/type/spatial/GeometryTest.java | 22 +++ 8 files changed, 324 insertions(+), 44 deletions(-) create mode 100644 core/src/main/java/org/polypheny/db/type/entity/spatial/PolyPolygon.java diff --git a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometry.java b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometry.java index c962049d2e..28c6a58685 100644 --- a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometry.java +++ b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometry.java @@ -37,6 +37,7 @@ import org.polypheny.db.type.PolySerializable; import org.polypheny.db.type.PolyType; import org.polypheny.db.type.entity.PolyValue; +import org.polypheny.db.type.entity.spatial.PolyGeometryType.BufferCapStyle; /** * {@link PolyGeometry} is an abstraction for all spatial data types. @@ -54,11 +55,6 @@ public class PolyGeometry extends PolyValue { /** * Wrap the JTS {@link Geometry} class. - *

- * It is possible to extract useful information from the {@link Geometry}: - * jtsGeometry.getGeometryType() - * jtsGeometry.getSRID() - * jtsGeometry.getCoordinate() */ @Getter protected Geometry jtsGeometry; @@ -71,14 +67,14 @@ public class PolyGeometry extends PolyValue { /** - * Constructor creates the {@link Geometry} from the WKT text. + * Constructor creates the {@link PolyGeometry} from the WKT text. *

* The WKT may contain SRID in the text, e.g. "SRID=4326;POINT (13.4050 52.5200)". * In this case SRID from the WKT would be used, otherwise the default one is selected. *

* * @param wkt Well Know Text representation of the geometry - * @throws InvalidGeometryException if {@link Geometry} is invalid or provided WKT is invalid. + * @throws InvalidGeometryException if {@link PolyGeometry} is invalid or provided WKT is invalid. */ public PolyGeometry( @JsonProperty("wkt") @Deserialize("wkt") String wkt ) throws InvalidGeometryException { this( PolyType.GEOMETRY ); @@ -95,21 +91,23 @@ public PolyGeometry( @JsonProperty("wkt") @Deserialize("wkt") String wkt ) throw } catch ( NumberFormatException e ) { throw new InvalidGeometryException( e.getMessage() ); } - init(wkt, SRID); + init( wkt, SRID ); } + /** - * Constructor creates the {@link Geometry} from the WKT text using the provided SRID. + * Constructor creates the {@link PolyGeometry} from the WKT text using the provided SRID. * * @param wkt Well Know Text representation of the geometry * @param SRID Spatial reference system of the geometry - * @throws InvalidGeometryException if {@link Geometry} is invalid or provided WKT is invalid. + * @throws InvalidGeometryException if {@link PolyGeometry} is invalid or provided WKT is invalid. */ public PolyGeometry( @JsonProperty("wkt") @Deserialize("wkt") String wkt, int SRID ) throws InvalidGeometryException { this( PolyType.GEOMETRY ); init( wkt, SRID ); } + public PolyGeometry( Geometry geometry ) { super( PolyType.GEOMETRY ); this.jtsGeometry = geometry; @@ -122,27 +120,12 @@ protected PolyGeometry( PolyType type ) { super( type ); } - private void init( String wkt, int SRID) throws InvalidGeometryException { - this.SRID = SRID; - WKTReader reader = new WKTReader(); - try { - this.jtsGeometry = reader.read( wkt ); - if ( !jtsGeometry.isValid() ) { - throw new ParseException( "Provided geometry is not valid." ); - } - this.jtsGeometry.setSRID( this.SRID ); - } catch ( ParseException | IllegalArgumentException e) { - // IllegalArgumentException is thrown in case geometry conditions are not met - throw new InvalidGeometryException( e.getMessage() ); - } - this.geometryType = getPolyGeometryType(); - } - public static PolyGeometry of( String wkt ) throws InvalidGeometryException { return new PolyGeometry( wkt ); } + public static PolyGeometry of( String wkt, int SRID ) throws InvalidGeometryException { return new PolyGeometry( wkt, SRID ); } @@ -153,6 +136,23 @@ public static PolyGeometry of( Geometry geometry ) { } + private void init( String wkt, int SRID ) throws InvalidGeometryException { + this.SRID = SRID; + WKTReader reader = new WKTReader(); + try { + this.jtsGeometry = reader.read( wkt ); + if ( !jtsGeometry.isValid() ) { + throw new ParseException( "Provided geometry is not valid." ); + } + this.jtsGeometry.setSRID( this.SRID ); + } catch ( ParseException | IllegalArgumentException e ) { + // IllegalArgumentException is thrown in case geometry conditions are not met + throw new InvalidGeometryException( e.getMessage() ); + } + this.geometryType = getPolyGeometryType(); + } + + protected PolyGeometryType getPolyGeometryType() { switch ( jtsGeometry.getGeometryType() ) { case "Point": @@ -161,6 +161,8 @@ protected PolyGeometryType getPolyGeometryType() { return PolyGeometryType.LINESTRING; case "LinearRing": return PolyGeometryType.LINEAR_RING; + case "Polygon": + return PolyGeometryType.POLYGON; default: throw new NotImplementedException( "value" ); } @@ -180,6 +182,7 @@ public PolyPoint asPoint() { throw cannotParse( this, PolyPoint.class ); } + public boolean isLineString() { return geometryType.equals( PolyGeometryType.LINESTRING ); } @@ -193,6 +196,7 @@ public PolyLineString asLineString() { throw cannotParse( this, PolyLineString.class ); } + public boolean isLinearRing() { return geometryType.equals( PolyGeometryType.LINEAR_RING ); } @@ -207,8 +211,23 @@ public PolyLinearRing asLinearRing() { } + public boolean isPolygon() { + return geometryType.equals( PolyGeometryType.POLYGON ); + } + + + @NotNull + public PolyPolygon asPolygon() { + if ( isPolygon() ) { + return PolyPolygon.of( jtsGeometry ); + } + throw cannotParse( this, PolyPolygon.class ); + } + + /** * Tests whether this {@link Geometry} is simple. + * * @return true if {@link Geometry} is simple. */ public boolean isSimple() { @@ -239,10 +258,16 @@ public int getDimension() { return jtsGeometry.getDimension(); } + // TODO: add geometry collections (@link) to documentation + /** * Linear geometries return their length. * Areal geometries return their perimeter. + * The length of a {@link PolyPoint} or {link PolyMultiPoint} is 0. + * The length of a {@link PolyLineString} is the sum of the lengths of each line segment: distance from the start point to the end point + * The length of a {@link PolyPolygon} is the sum of the lengths of the exterior boundary and any interior boundaries. + * The length of any {link GeometryCollection} is the sum of the lengths of all {@link PolyGeometry} it contains. * * @return the length of this {@link Geometry} */ @@ -262,17 +287,108 @@ public double getArea() { /** - * Bound the {@link Geometry} my the minimum box that could fit this geometry. + * Bound the {@link PolyGeometry} my the minimum box that could fit this geometry. * * @return {@link PolyGeometry} with minimum bounding box */ - public PolyGeometry getMinimumBoundingBox() { + public PolyGeometry getEnvelope() { return PolyGeometry.of( jtsGeometry.getEnvelope() ); } /** - * {@link #equals(Object) equals} ensures that the {@link Geometry} types and coordinates are the same. + * The boundary of a {@link PolyGeometry} is a set of {@link PolyGeometry}s of the next lower dimension + * that define the limit of this {@link PolyGeometry}. + * + * @return the closure of the combinatorial boundary of this {@link PolyGeometry} + */ + public PolyGeometry getBoundary() { + return PolyGeometry.of( jtsGeometry.getBoundary() ); + } + + + /** + * Calculate the smallest convex {@link PolyGeometry} that contains this {@link PolyGeometry}. + * @return the minimum area {@link PolyGeometry} containing all points in this {@link PolyGeometry} + */ + public PolyGeometry convexHull() { + return PolyGeometry.of( jtsGeometry.convexHull() ); + } + + + /** + * Computes the geometric center - centroid - of this {@link PolyGeometry}. + *

+ * When a geometry contains a combination of {@link PolyPoint}s, {@link PolyLineString}s, and {@link PolyPolygon}s, + * the centroid calculation is influenced solely by the {@link PolyPolygon}s within the {@link PolyGeometry}. + *

+ * Likewise, in the presence of both {@link PolyPoint}s and {@link PolyLineString}s within a {@link PolyGeometry}, + * the contribution of {@link PolyPoint}s to the centroid calculation is disregarded. + * + * @return {@link PolyPoint} that is the centroid of this {@link PolyGeometry} + */ + public PolyPoint getCentroid() { + return PolyPoint.of( jtsGeometry.getCentroid() ); + } + + + /** + * Compute a buffer area around this {@link PolyGeometry} within the given distance. + * The buffer maybe empty. + * The negative or zero-distance buffer of {@link PolyLineString}s and {@link PolyPoint}s + * is always an {@link PolyPolygon}. + * + * @param distance width of the buffer + * @return a {@link PolyPolygon} that represent buffer region around this {@link PolyGeometry} + */ + public PolyGeometry buffer( double distance ) { + return PolyPoint.of( jtsGeometry.buffer( distance ) ); + } + + + /** + * Compute a buffer area around this {@link PolyGeometry} within the given distance + * and accuracy of approximation for circular arcs. + * The buffer maybe empty. + * The negative or zero-distance buffer of {@link PolyLineString}s and {@link PolyPoint}s + * is always an {@link PolyPolygon}. + * + * @param distance width of the buffer + * @param quadrantSegments number of line segments to represent a quadrant of a circle + * @return a {@link PolyPolygon} that represent buffer region around this {@link PolyGeometry} + */ + public PolyGeometry buffer( double distance, int quadrantSegments ) { + return PolyPoint.of( jtsGeometry.buffer( distance, quadrantSegments ) ); + } + + + /** + * Compute a buffer area around this {@link PolyGeometry} within the given distance + * and accuracy of approximation for circular arcs, and use provided buffer end cap style. + * The buffer maybe empty. + * The negative or zero-distance buffer of {@link PolyLineString}s and {@link PolyPoint}s + * is an empty {@link PolyPolygon}. + * + * @param distance width of the buffer + * @param quadrantSegments number of line segments to represent a quadrant of a circle + * @param endCapStyle specifies how the buffer command terminates the end of a line. + * @return a {@link PolyPolygon} that represent buffer region around this {@link PolyGeometry} + */ + public PolyGeometry buffer( double distance, int quadrantSegments, BufferCapStyle endCapStyle ) { + return PolyPoint.of( jtsGeometry.buffer( distance, quadrantSegments, endCapStyle.code ) ); + } + + + /** + * @return new {@link PolyGeometry} with coordinates in a reverse order. + */ + public PolyGeometry reverse() { + return PolyGeometry.of( jtsGeometry.reverse() ); + } + + + /** + * {@link #equals(Object) equals} ensures that the {@link PolyGeometry} types and coordinates are the same. * And {@link #SRID} used for representation of coordinates are also identical. */ @Override @@ -321,7 +437,7 @@ public int compareTo( @NotNull PolyValue o ) { /** - * Output the {@link Geometry} in a WKT format with its SRID. So-called EWKT + * Output the {@link PolyGeometry} in a WKT format with its SRID. So-called EWKT */ @Override public String toString() { diff --git a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometryType.java b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometryType.java index 8d6b622991..56fdbcf773 100644 --- a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometryType.java +++ b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometryType.java @@ -17,11 +17,56 @@ package org.polypheny.db.type.entity.spatial; /** - * Represent Geometry types supported by Polypheny. + * Geometry types, with the names and codes assigned by OGC. */ public enum PolyGeometryType { + GEOMETRY( 0 ), + POINT( 1 ), + LINESTRING( 2 ), + // not actually a full type + LINEAR_RING( 2 ), + POLYGON(3), + MULTIPOINT( 4 ), + MULTILINESTRING( 5 ), + MULTIPOLYGON( 6 ), + GEOMCOLLECTION( 7 ), + CURVE( 13 ), + SURFACE( 14 ), + POLYHEDRALSURFACE( 15 ); - POINT, - LINESTRING, - LINEAR_RING + final int code; + + + PolyGeometryType( int code ) { + this.code = code; + } + + + /** + * How the "buffer" command terminates the end of a line. + */ + public enum BufferCapStyle { + ROUND( 1 ), FLAT( 2 ), SQUARE( 3 ); + + final int code; + + + BufferCapStyle( int code ) { + this.code = code; + } + + + static BufferCapStyle of( String value ) { + switch ( value ) { + case "round": + return ROUND; + case "flat": + return FLAT; + case "square": + return SQUARE; + default: + throw new IllegalArgumentException( "unknown endcap value: " + value ); + } + } + } } diff --git a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyLineString.java b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyLineString.java index bffbee4a19..c37b7dd44c 100644 --- a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyLineString.java +++ b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyLineString.java @@ -20,7 +20,6 @@ import io.activej.serializer.annotations.Deserialize; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.Point; import org.polypheny.db.type.PolyType; /** @@ -28,6 +27,7 @@ * The line segments may intersect other segments. * If line segments do not intersect each other, the line is simple * The line may start and end at the same point. In this case, it is called closed. + *

* The {@link PolyLineString} is valid if it has * either 0 or 2 or more points. * The {@link PolyLineString} could store up to 4 dimensions. @@ -44,6 +44,7 @@ public PolyLineString( @JsonProperty("wkt") @Deserialize("wkt") String wkt ) thr this.jtsLineString = (LineString) jtsGeometry; } + public PolyLineString( @JsonProperty("wkt") @Deserialize("wkt") String wkt, int SRID ) throws InvalidGeometryException { super( wkt, SRID ); this.geometryType = PolyGeometryType.LINESTRING; @@ -128,12 +129,4 @@ public PolyPoint getEndPoint() { return PolyPoint.of( jtsLineString.getEndPoint() ); } - - /** - * @return new {@link PolyLineString} with coordinates in a reverse order. - */ - public PolyLineString reverse() { - return PolyLineString.of( jtsLineString.reverse() ); - } - } diff --git a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyLinearRing.java b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyLinearRing.java index 9b96c29437..3277655f38 100644 --- a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyLinearRing.java +++ b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyLinearRing.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.activej.serializer.annotations.Deserialize; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.LinearRing; import org.polypheny.db.type.PolyType; @@ -32,12 +31,14 @@ public class PolyLinearRing extends PolyLineString { private LinearRing jtsLinearRing; + public PolyLinearRing( @JsonProperty("wkt") @Deserialize("wkt") String wkt ) throws InvalidGeometryException { super( wkt ); this.geometryType = PolyGeometryType.LINEAR_RING; this.jtsLinearRing = (LinearRing) jtsGeometry; } + public PolyLinearRing( @JsonProperty("wkt") @Deserialize("wkt") String wkt, int SRID ) throws InvalidGeometryException { super( wkt, SRID ); this.geometryType = PolyGeometryType.LINEAR_RING; @@ -59,6 +60,7 @@ protected PolyLinearRing( PolyType type ) { this.geometryType = PolyGeometryType.LINEAR_RING; } + public static PolyLinearRing of( Geometry geometry ) { return new PolyLinearRing( geometry ); } diff --git a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyPoint.java b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyPoint.java index 67a96c5864..5b49a4a751 100644 --- a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyPoint.java +++ b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyPoint.java @@ -24,6 +24,7 @@ /** * Represent a single point in the space. + *

* The {@link PolyPoint} is valid if * X and Y coordinates are provided. * The {@link PolyPoint} could store up to 4 dimensions @@ -32,18 +33,21 @@ public class PolyPoint extends PolyGeometry { private Point jtsPoint; + public PolyPoint( @JsonProperty("wkt") @Deserialize("wkt") String wkt ) throws InvalidGeometryException { super( wkt ); this.geometryType = PolyGeometryType.POINT; this.jtsPoint = (Point) jtsGeometry; } + public PolyPoint( @JsonProperty("wkt") @Deserialize("wkt") String wkt, int SRID ) throws InvalidGeometryException { super( wkt, SRID ); this.geometryType = PolyGeometryType.POINT; this.jtsPoint = (Point) jtsGeometry; } + protected PolyPoint( Geometry geometry ) { super( PolyType.GEOMETRY ); this.geometryType = PolyGeometryType.POINT; @@ -52,35 +56,43 @@ protected PolyPoint( Geometry geometry ) { this.SRID = geometry.getSRID(); } + protected PolyPoint( PolyType type ) { super( type ); this.geometryType = PolyGeometryType.POINT; } + public static PolyPoint of( Geometry geometry ) { return new PolyPoint( geometry ); } + public double getX() { return jtsPoint.getX(); } + public double getY() { return jtsPoint.getY(); } + public boolean hasZ() { return !Double.isNaN( jtsPoint.getCoordinate().getZ() ); } + public double getZ() { return jtsPoint.getCoordinate().getZ(); } + public boolean hasM() { return !Double.isNaN( jtsPoint.getCoordinate().getM() ); } + public double getM() { return jtsPoint.getCoordinate().getM(); } diff --git a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyPolygon.java b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyPolygon.java new file mode 100644 index 0000000000..39b652f419 --- /dev/null +++ b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyPolygon.java @@ -0,0 +1,89 @@ +/* + * Copyright 2019-2023 The Polypheny Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.polypheny.db.type.entity.spatial; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.activej.serializer.annotations.Deserialize; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.Polygon; +import org.polypheny.db.type.PolyType; + +/** + * Represent a polygon with linear edges, may contain holes. + * The outer boundary (shell) and inner boundaries (holes) are represented by {@link PolyLinearRing}. + * It means {@link PolyPolygon} is simple and closed. + * Interior holes should not split the {@link PolyPolygon} into more than one part. + *

+ * The {@link PolyPolygon} is valid if + * outer and inner boundaries are valid ({@link PolyLinearRing}) + * holes touch the shell or another hole at most one point + * Interior holes should not make the interior of the polygon disconnected + */ +public class PolyPolygon extends PolyGeometry { + + private Polygon jtsPolygon; + + + public PolyPolygon( @JsonProperty("wkt") @Deserialize("wkt") String wkt ) throws InvalidGeometryException { + super( wkt ); + this.geometryType = PolyGeometryType.POLYGON; + this.jtsPolygon = (Polygon) jtsGeometry; + } + + + public PolyPolygon( String wkt, int SRID ) throws InvalidGeometryException { + super( wkt, SRID ); + this.geometryType = PolyGeometryType.POLYGON; + this.jtsPolygon = (Polygon) jtsGeometry; + } + + + public PolyPolygon( Geometry geometry ) { + super( geometry ); + this.geometryType = PolyGeometryType.POLYGON; + this.jtsGeometry = geometry; + this.jtsPolygon = (Polygon) jtsGeometry; + this.SRID = geometry.getSRID(); + } + + + protected PolyPolygon( PolyType type ) { + super( type ); + this.geometryType = PolyGeometryType.POLYGON; + } + + + public static PolyPolygon of( Geometry geometry ) { + return new PolyPolygon( geometry ); + } + + + public PolyLinearRing getExteriorRing() { + return PolyLinearRing.of( jtsPolygon.getExteriorRing() ); + } + + + public int getNumInteriorRing() { + return jtsPolygon.getNumInteriorRing(); + } + + + public PolyLinearRing getInteriorRingN( int n ) { + return PolyLinearRing.of( jtsPolygon.getInteriorRingN( n ) ); + } + +} diff --git a/dbms/src/test/java/org/polypheny/db/type/spatial/GeometryConstants.java b/dbms/src/test/java/org/polypheny/db/type/spatial/GeometryConstants.java index d155ea6b2a..c3aace1a66 100644 --- a/dbms/src/test/java/org/polypheny/db/type/spatial/GeometryConstants.java +++ b/dbms/src/test/java/org/polypheny/db/type/spatial/GeometryConstants.java @@ -22,6 +22,7 @@ public class GeometryConstants { static final String POINT_WKT = "POINT (13.4050 52.5200 36.754)"; static final String LINESTRING_WKT = "LINESTRING(-1 -1, 2 2, 4 5, 6 7)"; static final String LINEAR_RING_WKT = "LINEARRING (0 0, 0 10, 10 10, 10 0, 0 0)"; + static final String POLYGON_WKT = "POLYGON((-1 -1, 2 2, -1 2, -1 -1))"; static final double DELTA = 1e-5; static final int NO_SRID = 0; } diff --git a/dbms/src/test/java/org/polypheny/db/type/spatial/GeometryTest.java b/dbms/src/test/java/org/polypheny/db/type/spatial/GeometryTest.java index fa41a0cf98..c5080ad9a0 100644 --- a/dbms/src/test/java/org/polypheny/db/type/spatial/GeometryTest.java +++ b/dbms/src/test/java/org/polypheny/db/type/spatial/GeometryTest.java @@ -35,6 +35,7 @@ import org.polypheny.db.type.entity.spatial.PolyLineString; import org.polypheny.db.type.entity.spatial.PolyLinearRing; import org.polypheny.db.type.entity.spatial.PolyPoint; +import org.polypheny.db.type.entity.spatial.PolyPolygon; public class GeometryTest { @@ -42,6 +43,7 @@ public class GeometryTest { private PolyGeometry point3d; private PolyGeometry lineString; private PolyGeometry linearRing; + private PolyGeometry polygon; @BeforeClass @@ -55,6 +57,7 @@ public void prepareGeometries() throws InvalidGeometryException { point3d = PolyGeometry.of( GeometryConstants.POINT_WKT ); lineString = PolyGeometry.of( GeometryConstants.LINESTRING_WKT ); linearRing = PolyGeometry.of( GeometryConstants.LINEAR_RING_WKT ); + polygon = PolyGeometry.of( GeometryConstants.POLYGON_WKT ); } @@ -125,6 +128,25 @@ public void testLinearRingValidity() { assertThrows( InvalidGeometryException.class, () -> PolyGeometry.of( "LINEARRING(0 0, 0 10, 10 10, 10 5, 5 5)" ) ); } + @Test + public void testPolygonValidity() { + assertAll( "Group assertions of valid Polygon", + () -> assertDoesNotThrow( () -> PolyGeometry.of( GeometryConstants.POLYGON_WKT ) ), + () -> { + assertEquals( PolyGeometryType.POLYGON, polygon.getGeometryType() ); + assertEquals( GeometryConstants.NO_SRID, (long) polygon.getSRID() ); + PolyPolygon poly = polygon.asPolygon(); + assertEquals( PolyPoint.of( "POINT (-0 1)" ), poly.getCentroid() ); + assertEquals( PolyLinearRing.of( "LINEARRING (-1 -1, 2 2, -1 2, -1 -1)" ), poly.getBoundary() ); + assertEquals( poly.getExteriorRing(), poly.getBoundary() ); + assertEquals( 10.2426406, poly.getLength(), GeometryConstants.DELTA ); + assertEquals( 4.5, poly.getArea(), GeometryConstants.DELTA ); + assertEquals( 4, poly.getNumPoints() ); + } ); + + assertThrows( InvalidGeometryException.class, () -> PolyGeometry.of( "POLYGON((-1 -1, 2 2, -1 1, -1 2, 2 2, -1 -1))" ) ); + } + @Test public void testPointsEquality() throws InvalidGeometryException {