diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFields.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFields.java index 48991459..a965ed20 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFields.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFields.java @@ -262,6 +262,15 @@ public Query getIntersectsQuery(String literal) { ._toQuery(); } + @Override + public Query getBoundingBoxQuery(TopLeftBottomRightGeoBounds tlbr) { + return new GeoBoundingBoxQuery.Builder() + .field(this.searchField) + .boundingBox(builder -> builder.tlbr(tlbr)) + .build() + ._toQuery(); + } + @Override public Query getIsNullQuery() { Query fieldExist = ExistsQuery.of(f -> f diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFieldsInterface.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFieldsInterface.java index 50d4380e..ffcfdcca 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFieldsInterface.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/model/enumeration/CQLFieldsInterface.java @@ -2,6 +2,7 @@ import co.elastic.clients.elasticsearch._types.SortOptions; import co.elastic.clients.elasticsearch._types.SortOrder; +import co.elastic.clients.elasticsearch._types.TopLeftBottomRightGeoBounds; import co.elastic.clients.elasticsearch._types.query_dsl.Query; import co.elastic.clients.util.ObjectBuilder; @@ -16,4 +17,5 @@ public interface CQLFieldsInterface { Query getIsNullQuery(); Query getLikeQuery(String literal); Query getPropertyGreaterThanOrEqualsToQuery(String literal); + Query getBoundingBoxQuery(TopLeftBottomRightGeoBounds tlbr); } diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/BBoxImpl.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/BBoxImpl.java new file mode 100644 index 00000000..24578636 --- /dev/null +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/BBoxImpl.java @@ -0,0 +1,147 @@ +package au.org.aodn.ogcapi.server.core.parser.elastic; + +import au.org.aodn.ogcapi.server.core.model.enumeration.CQLCrsType; +import au.org.aodn.ogcapi.server.core.model.enumeration.CQLFieldsInterface; +import co.elastic.clients.elasticsearch._types.TopLeftBottomRightGeoBounds; +import org.geotools.filter.spatial.BBOXImpl; +import org.geotools.geometry.jts.JTS; +import org.geotools.geometry.jts.JTSFactoryFinder; +import org.geotools.geometry.jts.ReferencedEnvelope; +import org.geotools.geometry.jts.ReferencedEnvelope3D; +import org.geotools.referencing.CRS; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.opengis.filter.FilterVisitor; +import org.opengis.filter.MultiValuedFilter; +import org.opengis.filter.expression.Expression; +import org.opengis.filter.expression.Literal; +import org.opengis.filter.expression.PropertyName; +import org.opengis.filter.spatial.BBOX; +import org.opengis.geometry.BoundingBox; +import org.opengis.geometry.BoundingBox3D; +import org.opengis.geometry.MismatchedDimensionException; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.NoSuchAuthorityCodeException; +import org.opengis.referencing.crs.CoordinateReferenceSystem; + +/** + * This class support both 2D or 3D query, but now we just implement 2D and support very limited operation for CQL + * which is sufficient for init run. + * example: BBOX(geometry, 105.08984375000037, -45.407201919778046, 163.91015625000117, -5.592798080220254) + * @param + */ +public class BBoxImpl & CQLFieldsInterface> extends QueryHandler implements BBOX { + protected static final GeometryFactory factory = JTSFactoryFinder.getGeometryFactory(); + + protected Expression geometry = null; + protected BoundingBox bounds = null; + protected MultiValuedFilter.MatchAction matchAction = MatchAction.ANY; + + public BBoxImpl(Expression geometry, + BoundingBox bounds, + MultiValuedFilter.MatchAction matchAction, + Class enumType) { + + if(bounds instanceof BoundingBox3D box3D) { + this.create3DCQL(geometry, box3D, matchAction); + } + else { + this.create2DCQL(geometry, bounds, matchAction, enumType); + } + } + + public BBoxImpl(Expression geometry, Expression bounds, MultiValuedFilter.MatchAction matchAction) { + if (bounds instanceof Literal literal) { + Object value = literal.getValue(); + if (value instanceof BoundingBox3D boundingBox3D) { + this.create3DCQL(geometry, boundingBox3D, matchAction); + } + else if (value instanceof Geometry g && geometry instanceof PropertyName name) { + if (g.getUserData() instanceof CoordinateReferenceSystem crs) { + this.create3DCQL(name, (ReferencedEnvelope3D) JTS.bounds(g, crs), matchAction); + } + } + } + } + + public BBoxImpl( + Expression e, + double minx, double miny, double maxx, double maxy, String srs, + MultiValuedFilter.MatchAction matchAction, + Class enumType) { + try { + CoordinateReferenceSystem crs = null; + CQLCrsType cqlCrsType = CQLCrsType.EPSG4326; + if (srs != null && !srs.isEmpty()) { + try { + crs = CRS.decode(srs); + cqlCrsType = CQLCrsType.convertFromUrl(srs); + } + catch (MismatchedDimensionException mde) { + throw new RuntimeException(mde); + } + catch (NoSuchAuthorityCodeException var16) { + crs = CRS.parseWKT(srs); + } + } else { + crs = null; + } + this.bounds = new ReferencedEnvelope(minx, maxx, miny, maxy, crs); + this.create2DCQL(e, bounds , matchAction, enumType); + + } catch (FactoryException fe) { + throw new RuntimeException("Failed to setup bbox SRS", fe); + } + + } + + protected void create2DCQL( + Expression geometry, + BoundingBox bounds, + MultiValuedFilter.MatchAction matchAction, + Class enumType) { + + this.matchAction = matchAction; + this.geometry = geometry; + T v = Enum.valueOf(enumType, geometry.toString().toLowerCase()); + this.query = v.getBoundingBoxQuery( + TopLeftBottomRightGeoBounds.of(builder -> builder + .topLeft(i -> i.latlon(ll -> ll.lon(bounds.getMinX()).lat(bounds.getMaxY()))) + .bottomRight(i -> i.latlon(ll -> ll.lon(bounds.getMaxX()).lat(bounds.getMinY()))))); + } + + protected void create3DCQL(Expression geometry, BoundingBox3D bounds, MultiValuedFilter.MatchAction matchAction) { + this.matchAction = matchAction; + + } + + @Override + public BoundingBox getBounds() { + return bounds; + } + + @Override + public Expression getExpression1() { + return geometry; + } + + @Override + public Expression getExpression2() { + return null; + } + + @Override + public MatchAction getMatchAction() { + return this.matchAction; + } + + @Override + public boolean evaluate(Object o) { + return false; + } + + @Override + public Object accept(FilterVisitor filterVisitor, Object o) { + return null; + } +} diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/CQLToElasticFilterFactory.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/CQLToElasticFilterFactory.java index d3e1cad7..f695a271 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/CQLToElasticFilterFactory.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/elastic/CQLToElasticFilterFactory.java @@ -406,32 +406,54 @@ public PropertyIsNil isNil(Expression expression, Object o) { } @Override - public BBOX bbox(String s, double v, double v1, double v2, double v3, String s1) { - return null; + public BBOX bbox(String propertyName, double minx, double miny, double maxx, double maxy, String srs) { + PropertyName name = this.property(propertyName); + return this.bbox(name, minx, miny, maxx, maxy, srs); } @Override - public BBOX bbox(Expression expression, Expression expression1) { - return null; + public BBOX bbox(Expression geometry, Expression bounds) { + return this.bbox(geometry, bounds, MultiValuedFilter.MatchAction.ANY); } @Override - public BBOX bbox(Expression expression, Expression expression1, MultiValuedFilter.MatchAction matchAction) { - return null; + public BBOX bbox(Expression geometry, Expression bounds, MultiValuedFilter.MatchAction matchAction) { + return new BBoxImpl<>(geometry, bounds, matchAction); } @Override - public BBOX3D bbox(String s, BoundingBox3D boundingBox3D) { - return null; + public BBOX bbox(Expression expression, double minx, double miny, double maxx, double maxy, String srs) { + return this.bbox(expression, minx, miny, maxx, maxy, srs, MultiValuedFilter.MatchAction.ANY); } @Override - public BBOX3D bbox(String s, BoundingBox3D boundingBox3D, MultiValuedFilter.MatchAction matchAction) { + public BBOX bbox(String propertyName, double minx, double miny, double maxx, double maxy, String srs, MultiValuedFilter.MatchAction matchAction) { + PropertyName name = this.property(propertyName); + return this.bbox(name, minx, miny, maxx, maxy, srs, matchAction); + } + + @Override + public BBOX bbox(Expression expression, double minx, double miny, double maxx, double maxy, String srs, MultiValuedFilter.MatchAction matchAction) { + return new BBoxImpl<>(expression, minx, miny, maxx, maxy, srs, matchAction, collectionFieldType); + } + + @Override + public BBOX bbox(Expression geometry, BoundingBox bounds) { + return this.bbox(geometry, bounds, MultiValuedFilter.MatchAction.ANY); + } + + @Override + public BBOX bbox(Expression geometry, BoundingBox bounds, MultiValuedFilter.MatchAction matchAction) { + return new BBoxImpl<>(geometry, bounds, matchAction, collectionFieldType); + } + + @Override + public BBOX3D bbox(String propertyName, BoundingBox3D boundingBox3D) { return null; } @Override - public BBOX bbox(String s, double v, double v1, double v2, double v3, String s1, MultiValuedFilter.MatchAction matchAction) { + public BBOX3D bbox(String s, BoundingBox3D boundingBox3D, MultiValuedFilter.MatchAction matchAction) { return null; } @@ -830,16 +852,6 @@ public FilterCapabilities capabilities(String s, ScalarCapabilities scalarCapabi return null; } - @Override - public BBOX bbox(Expression expression, double v, double v1, double v2, double v3, String s) { - return null; - } - - @Override - public BBOX bbox(Expression expression, double v, double v1, double v2, double v3, String s, MultiValuedFilter.MatchAction matchAction) { - return null; - } - @Override public BBOX3D bbox(Expression expression, BoundingBox3D boundingBox3D) { return null; @@ -850,16 +862,6 @@ public BBOX3D bbox(Expression expression, BoundingBox3D boundingBox3D, MultiValu return null; } - @Override - public BBOX bbox(Expression expression, BoundingBox boundingBox) { - return null; - } - - @Override - public BBOX bbox(Expression expression, BoundingBox boundingBox, MultiValuedFilter.MatchAction matchAction) { - return null; - } - @Override public Beyond beyond(Expression expression, Expression expression1, double v, String s) { return null; diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/stac/BBoxImpl.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/stac/BBoxImpl.java new file mode 100644 index 00000000..7ce815eb --- /dev/null +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/stac/BBoxImpl.java @@ -0,0 +1,101 @@ +package au.org.aodn.ogcapi.server.core.parser.stac; + +import au.org.aodn.ogcapi.server.core.model.enumeration.CQLCrsType; +import au.org.aodn.ogcapi.server.core.model.enumeration.CQLFieldsInterface; +import au.org.aodn.ogcapi.server.core.util.GeometryUtils; +import lombok.Getter; +import org.geotools.geometry.jts.ReferencedEnvelope; +import org.geotools.referencing.CRS; +import org.opengis.filter.FilterVisitor; +import org.opengis.filter.MultiValuedFilter; +import org.opengis.filter.expression.Expression; +import org.opengis.filter.expression.Literal; +import org.opengis.filter.expression.PropertyName; +import org.opengis.filter.spatial.BBOX; +import org.opengis.geometry.BoundingBox; +import org.opengis.geometry.BoundingBox3D; +import org.opengis.geometry.MismatchedDimensionException; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.NoSuchAuthorityCodeException; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.locationtech.jts.geom.Geometry; + +public class BBoxImpl & CQLFieldsInterface> implements BBOX { + + @Getter + protected Geometry geometry = null; + + protected BoundingBox bounds = null; + + public BBoxImpl(Expression geometry, Expression bounds, MultiValuedFilter.MatchAction matchAction) { + if (bounds instanceof Literal literal) { + Object value = literal.getValue(); + if (value instanceof BoundingBox3D boundingBox3D) { + } + else if (value instanceof Geometry g && geometry instanceof PropertyName name) { + if (g.getUserData() instanceof CoordinateReferenceSystem crs) { + } + } + } + } + + public BBoxImpl( + Expression e, + double minx, double miny, double maxx, double maxy, String srs, + MultiValuedFilter.MatchAction matchAction) { + try { + CoordinateReferenceSystem crs = null; + CQLCrsType cqlCrsType = CQLCrsType.EPSG4326; + if (srs != null && !srs.isEmpty()) { + try { + crs = CRS.decode(srs); + cqlCrsType = CQLCrsType.convertFromUrl(srs); + } + catch (MismatchedDimensionException mde) { + throw new RuntimeException(mde); + } + catch (NoSuchAuthorityCodeException var16) { + crs = CRS.parseWKT(srs); + } + } else { + crs = null; + } + this.bounds = new ReferencedEnvelope(minx, maxx, miny, maxy, crs); + this.geometry = GeometryUtils.normalizePolygon(GeometryUtils.createPolygon(minx, maxx, miny, maxy)); + + } catch (FactoryException fe) { + throw new RuntimeException("Failed to setup bbox SRS", fe); + } + + } + + @Override + public BoundingBox getBounds() { + return this.bounds; + } + + @Override + public Expression getExpression1() { + return null; + } + + @Override + public Expression getExpression2() { + return null; + } + + @Override + public MatchAction getMatchAction() { + return null; + } + + @Override + public boolean evaluate(Object o) { + return false; + } + + @Override + public Object accept(FilterVisitor visitor, Object extraData) { + return visitor.visit(this, extraData); + } +} diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/stac/CQLToStacFilterFactory.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/stac/CQLToStacFilterFactory.java index a3aaade7..71f3c8b4 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/stac/CQLToStacFilterFactory.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/stac/CQLToStacFilterFactory.java @@ -273,32 +273,64 @@ public PropertyIsNil isNil(Expression expression, Object o) { } @Override - public BBOX bbox(String s, double v, double v1, double v2, double v3, String s1) { + public BBOX bbox(String propertyName, double minx, double miny, double maxx, double maxy, String srs) { + PropertyName name = this.property(propertyName); + return this.bbox(name, minx, miny, maxx, maxy, srs); + } + + @Override + public BBOX bbox(Expression geometry, Expression bounds) { + return this.bbox(geometry, bounds, MultiValuedFilter.MatchAction.ANY); + } + + @Override + public BBOX bbox(Expression geometry, Expression bounds, MultiValuedFilter.MatchAction matchAction) { + return new BBoxImpl<>(geometry, bounds, matchAction); + } + + @Override + public BBOX bbox(String propertyName, double minx, double miny, double maxx, double maxy, String srs, MultiValuedFilter.MatchAction matchAction) { + PropertyName name = this.property(propertyName); + return this.bbox(name, minx, miny, maxx, maxy, srs, matchAction); + } + + @Override + public BBOX bbox(Expression expression, double minx, double miny, double maxx, double maxy, String srs) { + return this.bbox(expression, minx, miny, maxx, maxy, srs, MultiValuedFilter.MatchAction.ANY); + } + + @Override + public BBOX bbox(Expression expression, double minx, double miny, double maxx, double maxy, String srs, MultiValuedFilter.MatchAction matchAction) { + return new BBoxImpl<>(expression, minx, miny, maxx, maxy, srs, matchAction); + } + + @Override + public BBOX bbox(Expression expression, BoundingBox boundingBox) { return null; } @Override - public BBOX bbox(Expression expression, Expression expression1) { + public BBOX bbox(Expression expression, BoundingBox boundingBox, MultiValuedFilter.MatchAction matchAction) { return null; } @Override - public BBOX bbox(Expression expression, Expression expression1, MultiValuedFilter.MatchAction matchAction) { + public BBOX3D bbox(Expression expression, BoundingBox3D boundingBox3D) { return null; } @Override - public BBOX3D bbox(String s, BoundingBox3D boundingBox3D) { + public BBOX3D bbox(Expression expression, BoundingBox3D boundingBox3D, MultiValuedFilter.MatchAction matchAction) { return null; } @Override - public BBOX3D bbox(String s, BoundingBox3D boundingBox3D, MultiValuedFilter.MatchAction matchAction) { + public BBOX3D bbox(String s, BoundingBox3D boundingBox3D) { return null; } @Override - public BBOX bbox(String s, double v, double v1, double v2, double v3, String s1, MultiValuedFilter.MatchAction matchAction) { + public BBOX3D bbox(String s, BoundingBox3D boundingBox3D, MultiValuedFilter.MatchAction matchAction) { return null; } @@ -703,36 +735,6 @@ public FilterCapabilities capabilities(String s, ScalarCapabilities scalarCapabi return null; } - @Override - public BBOX bbox(Expression expression, double v, double v1, double v2, double v3, String s) { - return null; - } - - @Override - public BBOX bbox(Expression expression, double v, double v1, double v2, double v3, String s, MultiValuedFilter.MatchAction matchAction) { - return null; - } - - @Override - public BBOX3D bbox(Expression expression, BoundingBox3D boundingBox3D) { - return null; - } - - @Override - public BBOX3D bbox(Expression expression, BoundingBox3D boundingBox3D, MultiValuedFilter.MatchAction matchAction) { - return null; - } - - @Override - public BBOX bbox(Expression expression, BoundingBox boundingBox) { - return null; - } - - @Override - public BBOX bbox(Expression expression, BoundingBox boundingBox, MultiValuedFilter.MatchAction matchAction) { - return null; - } - @Override public Beyond beyond(Expression expression, Expression expression1, double v, String s) { return null; diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/stac/GeometryVisitor.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/stac/GeometryVisitor.java index d5026a17..6a82810c 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/stac/GeometryVisitor.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/parser/stac/GeometryVisitor.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.geotools.filter.visitor.DefaultFilterVisitor; import org.locationtech.jts.geom.*; +import org.opengis.filter.spatial.BBOX; import org.opengis.filter.spatial.Intersects; @Slf4j @@ -26,4 +27,17 @@ public Object visit(Intersects filter, Object data) { } return null; } + + @Override + public Object visit(BBOX filter, Object data) { + if(filter instanceof BBoxImpl impl) { + if(impl.getBounds() != null && data instanceof Polygon || data instanceof GeometryCollection) { + return impl.getGeometry().intersection(((Geometry) data).buffer(0.0)); + } + else { + return data; + } + } + return null; + } } diff --git a/server/src/main/java/au/org/aodn/ogcapi/server/core/util/GeometryUtils.java b/server/src/main/java/au/org/aodn/ogcapi/server/core/util/GeometryUtils.java index 152f635f..d366fde1 100644 --- a/server/src/main/java/au/org/aodn/ogcapi/server/core/util/GeometryUtils.java +++ b/server/src/main/java/au/org/aodn/ogcapi/server/core/util/GeometryUtils.java @@ -220,4 +220,20 @@ public static Geometry normalizePolygon(Geometry polygon) { JtsGeometry jtsGeometry = new JtsGeometry(polygon, JtsSpatialContext.GEO, true, false); return jtsGeometry.getGeom(); } + + public static Geometry createPolygon(double minx, double maxx, double miny, double maxy) { + // Define the corners of the bounding box + Coordinate[] coordinates = new Coordinate[] { + new Coordinate(minx, maxy), // Top-Left + new Coordinate(maxx, maxy), // Top-Right + new Coordinate(maxx, miny), // Bottom-Right + new Coordinate(minx, miny), // Bottom-Left + new Coordinate(minx, maxy) // Closing the polygon (back to Top-Left) + }; + // Create a LinearRing (boundary of the polygon) + LinearRing shell = factory.createLinearRing(coordinates); + + // Create the polygon (no holes) + return factory.createPolygon(shell, null); + } } diff --git a/server/src/test/java/au/org/aodn/ogcapi/server/core/parser/stac/ParserTest.java b/server/src/test/java/au/org/aodn/ogcapi/server/core/parser/stac/ParserTest.java index 0b2441c6..d9c73f38 100644 --- a/server/src/test/java/au/org/aodn/ogcapi/server/core/parser/stac/ParserTest.java +++ b/server/src/test/java/au/org/aodn/ogcapi/server/core/parser/stac/ParserTest.java @@ -61,7 +61,7 @@ public void verifyIntersectionWorks1() throws CQLException, IOException, Factory .coordinationSystem(CQLCrsType.EPSG4326) .filter("score>=1.5 AND INTERSECTS(geometry,POLYGON ((106.60546875000024 -40.43564679957577, 167.42578125 -40.43564679957577, 167.42578125 -6.4339250592726005, 106.60546875000024 -6.4339250592726005, 106.60546875000024 -40.43564679957577)))") .build(); - + // Expected polygon shape after the no land area intersection with the POLYGON Optional expected = GeometryUtils.readGeometry( GeometryUtils.convertToGeoJson( new LiteralExpressionImpl( @@ -86,7 +86,48 @@ public void verifyIntersectionWorks1() throws CQLException, IOException, Factory Assertions.assertTrue(expected.isPresent(), "Expected parse correct"); Assertions.assertEquals(g, expected.get(), "They are equals"); } + /** + * Test almost the same as the verifyIntersectionWorks1, since verifyIntersectionWorks1 create a polygon same as box + * so use Intersect or BBox will result in same result + * + * @throws CQLException - Will not throw + * @throws IOException - Will not throw + * @throws FactoryException - Will not throw + * @throws TransformException - Will not throw + * @throws ParseException - Will not throw + */ + @Test + public void verifyBBoxWorks1() throws CQLException, IOException, FactoryException, TransformException, ParseException { + // Assume we have the following CQL + Converter.Param param = Converter.Param.builder() + .coordinationSystem(CQLCrsType.EPSG4326) + .filter("score>=1.5 AND BBOX(geometry,106.60546875000024,-40.43564679957577,167.42578125,-6.4339250592726005)") + .build(); + // Expected polygon shape after the no land area intersection with the BBOX + Optional expected = GeometryUtils.readGeometry( + GeometryUtils.convertToGeoJson( + new LiteralExpressionImpl( + "POLYGON ((116 -34.84004284124928, 116 -36, 112 -36, 112 -27, 113.78063324619302 -27, 114.01254316500001 -27.31536223799992, 114.14389082100001 -27.687758070999905, 114.09791100400003 -27.862725518999923, 114.16602623800009 -28.10670338299991, 114.53109785200002 -28.52239348799992, 114.60377037900003 -28.838555596999925, 114.84245853000004 -29.108575127999927, 114.97877037900003 -29.479913018999923, 114.94263756600003 -30.031019789999903, 115.06080162900003 -30.52239348799992, 115.72152754000001 -31.772637627999927, 115.75684655000009 -32.18564218499995, 115.67367597700002 -32.273370049999926, 115.73585045700008 -32.33318450299993, 115.71452884200005 -32.537041924999926, 115.76929772200003 -32.60230885199991, 115.65886478000004 -32.62851327899995, 115.71412194100003 -32.78045012799993, 115.63965905000009 -32.656914971999925, 115.70093834700003 -32.51295338299991, 115.60092207100001 -32.66171640399995, 115.66684004000001 -33.28769296699994, 115.69792728000004 -33.1931291649999, 115.71013431100005 -33.27068450299993, 115.36719811300009 -33.63128020599993, 115.19467207100001 -33.64462655999995, 114.98853600400003 -33.52109140399995, 115.01042728000004 -34.24504973799992, 115.11882571700005 -34.36337655999995, 115.29078209700003 -34.301853122999944, 115.61231530000009 -34.44589609199994, 115.91578209700003 -34.70191822699991, 115.97852623800009 -34.83562590899993, 116 -34.84004284124928), (115.49210733500001 -31.99564901299993, 115.44014224400006 -32.021833949999916, 115.55219218900004 -32.00620348499996, 115.49210733500001 -31.99564901299993))"), + CQLCrsType.EPSG4326 + ) + ); + // Parse the json and get the noland section + String json = BaseTestClass.readResourceFile("classpath:databag/00462296-be7a-452f-afaf-36c809cd51f8.json"); + StacCollectionModel model = mapper.readValue(json, StacCollectionModel.class); + + Filter filter = CompilerUtil.parseFilter(Language.CQL, param.getFilter(), factory); + Optional geo = GeometryUtils.readGeometry(model.getSummaries().getGeometryNoLand()); + + Assertions.assertTrue(geo.isPresent(), "Parse no land correct"); + GeometryVisitor visitor = GeometryVisitor.builder() + .build(); + + // return value are geo applied the CQL, and in this case only INTERSECTS + Geometry g = (Geometry)filter.accept(visitor, geo.get()); + Assertions.assertTrue(expected.isPresent(), "Expected parse correct"); + Assertions.assertEquals(g, expected.get(), "They are equals"); + } /** * Test case where POLYGON cross the -180 line, we should be able to handle it correctly. * the parser will split the polygon into two and then apply the intersection with the noloand in json sample @@ -125,4 +166,41 @@ public void verifyIntersectionWorks2() throws CQLException, IOException, Factory Assertions.assertEquals(g.getCentroid().getX(), 168.30090846621448, "getX()"); Assertions.assertEquals(g.getCentroid().getY(), -33.95984804960966, "getY()"); } + /** + * Test almost the same as the verifyIntersectionWorks2, since verifyIntersectionWorks1 create a polygon same as box + * so use Intersect or BBox will result in same result + * + * @throws CQLException - Will not throw + * @throws IOException - Will not throw + * @throws FactoryException - Will not throw + * @throws TransformException - Will not throw + * @throws ParseException - Will not throw + */ + @Test + public void verifyBBoxWorks2() throws CQLException, IOException, FactoryException, TransformException, ParseException { + + // Parse the json and get the noland section + String json = BaseTestClass.readResourceFile("classpath:databag/0015db7e-e684-7548-e053-08114f8cd4ad.json"); + StacCollectionModel model = mapper.readValue(json, StacCollectionModel.class); + + Filter filter = CompilerUtil.parseFilter( + Language.CQL, + "score>=1.5 AND BBOX(geometry,-203.16603491348164,-60.248194404495756,-86.85117538227594,15.902738674628525)", + factory); + + Optional geo = GeometryUtils.readGeometry(model.getSummaries().getGeometryNoLand()); + + Assertions.assertTrue(geo.isPresent(), "Parse no land correct"); + GeometryVisitor visitor = GeometryVisitor.builder() + .build(); + + // return value are geo applied the CQL, and in this case only BBOX intersected + Geometry g = (Geometry)filter.accept(visitor, geo.get()); + + Assertions.assertFalse(g.isEmpty()); + Assertions.assertTrue(g instanceof Polygon); + + Assertions.assertEquals(g.getCentroid().getX(), 168.30090846621448, 0.0000001, "getX()"); + Assertions.assertEquals(g.getCentroid().getY(), -33.95984804960966, 0.0000001, "getY()"); + } }