diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/CentroidFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/CentroidFFTest.java index 685bc33cbc5..5274baf0074 100644 --- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/CentroidFFTest.java +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/CentroidFFTest.java @@ -23,7 +23,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -34,7 +33,6 @@ import org.apache.jena.geosparql.implementation.jts.CoordinateSequenceDimensions; import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; -import org.apache.jena.query.QueryBuildException; import org.apache.jena.query.QueryExecution; import org.apache.jena.query.QuerySolution; import org.apache.jena.query.ResultSet; @@ -114,6 +112,19 @@ public void projectedCrsIsPreserved() { assertEquals(300, result.getXYGeometry().getCoordinate().getY(), 0); } + @Test + public void directExecutionAcceptsExplicitWktCrs() { + NodeValue input = NodeValue.makeNode( + " LINESTRING (100 200, 300 400)", WKTDatatype.INSTANCE); + NodeValue output = new CentroidFF().exec(input); + assertEquals(WKTDatatype.URI, output.asNode().getLiteralDatatypeURI()); + GeometryWrapper result = GeometryWrapper.extract(output.asNode()); + assertPointMetadata(result); + assertEquals("http://www.opengis.net/def/crs/EPSG/0/27700", result.getSrsURI()); + assertEquals(200, result.getXYGeometry().getCoordinate().getX(), 0); + assertEquals(300, result.getXYGeometry().getCoordinate().getY(), 0); + } + @Test public void geographicCentroidIsPlanar() { assertCentroid("MULTIPOINT ((170 10), (-170 10))", 0, 10); @@ -194,16 +205,6 @@ public void invalidArgumentsRaiseExpressionErrors() { NodeValue.makeNode(NodeFactory.createURI("urn:geometry")), NodeValue.makeNode("invalid", WKTDatatype.INSTANCE) }) { assertThrows(ExprEvalException.class, () -> function.exec(value)); } - for (String value : new String[] { "42", "'POINT (1 2)'", "", "'invalid'^^geo:wktLiteral", "?missing" }) { - assertNull(evaluate("geof:centroid(" + value + ")")); - } - } - - @Test - public void wrongArityIsRejectedAtQueryBuild() { - assertThrows(QueryBuildException.class, () -> evaluate("geof:centroid()")); - assertThrows(QueryBuildException.class, - () -> evaluate("geof:centroid('POINT EMPTY'^^geo:wktLiteral, 1)")); } private static void assertCentroid(String wkt, double x, double y) { diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryCoordinateExtremaFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryCoordinateExtremaFFTest.java deleted file mode 100644 index 418893b171a..00000000000 --- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryCoordinateExtremaFFTest.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * https://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. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertThrows; -import static org.junit.Assume.assumeFalse; - -import java.util.List; - -import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; -import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; -import org.apache.jena.graph.Node; -import org.apache.jena.graph.NodeFactory; -import org.apache.jena.query.QueryBuildException; -import org.apache.jena.query.QueryExecution; -import org.apache.jena.query.QuerySolution; -import org.apache.jena.query.ResultSet; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.sparql.expr.ExprEvalException; -import org.apache.jena.sparql.expr.NodeValue; -import org.apache.jena.sparql.function.FunctionBase1; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -@RunWith(Parameterized.class) -public class GeometryCoordinateExtremaFFTest { - @Parameterized.Parameters(name = "function: {0}") - public static List functions() { - return List.of( - new Object[] { "minX", new MinXFF() }, - new Object[] { "minY", new MinYFF() }, - new Object[] { "minZ", new MinZFF() }, - new Object[] { "maxX", new MaxXFF() }, - new Object[] { "maxY", new MaxYFF() }, - new Object[] { "maxZ", new MaxZFF() } - ); - } - - private final String name; - private final FunctionBase1 function; - - public GeometryCoordinateExtremaFFTest(String name, FunctionBase1 function) { - this.name = name; - this.function = function; - } - - @BeforeClass - public static void setup() { - GeoSPARQLConfig.setupNoIndex(); - } - - @Test - public void iriArgumentRaisesExpressionError() { - assertThrows(ExprEvalException.class, () -> function.exec(NodeValue.makeNode(NodeFactory.createURI("urn:not-a-literal")))); - } - - @Test - public void numericArgumentRaisesExpressionError() { - assertThrows(ExprEvalException.class, () -> function.exec(NodeValue.makeInteger(42))); - } - - @Test - public void stringWithoutGeometryDatatypeRaisesExpressionError() { - assertThrows(ExprEvalException.class, () -> function.exec(NodeValue.makeString("POINT (1 2)"))); - } - - @Test - public void malformedWktRaisesExpressionError() { - NodeValue malformed = NodeValue.makeNode("invalid", WKTDatatype.INSTANCE); - assertThrows(ExprEvalException.class, () -> function.exec(malformed)); - } - - @Test - public void iriArgumentLeavesBindUnbound() { - assertUnbound(""); - } - - @Test - public void numericArgumentLeavesBindUnbound() { - assertUnbound("42"); - } - - @Test - public void stringWithoutGeometryDatatypeLeavesBindUnbound() { - assertUnbound("'POINT (1 2)'"); - } - - @Test - public void malformedWktLeavesBindUnbound() { - assertUnbound("'invalid'^^geo:wktLiteral"); - } - - @Test - public void unboundArgumentLeavesBindUnbound() { - assertUnbound("?missing"); - } - - @Test - public void missingArgumentIsRejectedAtQueryBuild() { - assertThrows(QueryBuildException.class, () -> evaluate("geof:" + name + "()")); - } - - @Test - public void extraArgumentIsRejectedAtQueryBuild() { - assertThrows(QueryBuildException.class, - () -> evaluate("geof:" + name + "('POINT EMPTY'^^geo:wktLiteral, 1)")); - } - - @Test - public void extremaReturnDoubleLiterals() { - double expected = switch (name) { - case "minX" -> -9; - case "maxX" -> -2; - case "minY" -> 3; - case "maxY" -> 8; - case "minZ" -> -7; - case "maxZ" -> -1; - default -> throw new AssertionError(name); - }; - assertEquals(NodeValue.makeDouble(expected).asNode(), - evaluate("geof:" + name + "('LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)'^^geo:wktLiteral)")); - } - - @Test - public void epsg4326LineStringExtremaFollowSrsDimensionOrder() { - String argument = "' " - + "LINESTRING (10 100, 20 120)'^^geo:wktLiteral"; - if (name.endsWith("Z")) { - assertUnbound(argument); - } else { - double expected = switch (name) { - case "minX" -> 10; - case "maxX" -> 20; - case "minY" -> 100; - case "maxY" -> 120; - default -> throw new AssertionError(name); - }; - assertEquals(NodeValue.makeDouble(expected).asNode(), - evaluate("geof:" + name + "(" + argument + ")")); - } - } - - @Test - public void infiniteOrdinateRaisesExpressionErrorAndLeavesBindUnbound() { - for (String infinity : new String[] { "Infinity", "-Infinity" }) { - String coordinate = switch (name.charAt(name.length() - 1)) { - case 'X' -> infinity + " 1 1"; - case 'Y' -> "1 " + infinity + " 1"; - case 'Z' -> "1 1 " + infinity; - default -> throw new AssertionError(name); - }; - String wkt = "LINESTRING Z (-4 -4 -4, " + coordinate + ", 9 9 9)"; - assertThrows(wkt, ExprEvalException.class, - () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); - assertUnbound("'" + wkt + "'^^geo:wktLiteral"); - } - } - - @Test - public void nanXYOrdinateRaisesExpressionErrorAndLeavesBindUnbound() { - assumeFalse("Only X/Y extrema are covered by this test", name.endsWith("Z")); - String coordinate = name.endsWith("Y") ? "2 NaN" : "NaN 2"; - String wkt = "LINESTRING (1 1, " + coordinate + ", 3 3)"; - assertThrows(wkt, ExprEvalException.class, - () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); - assertUnbound("'" + wkt + "'^^geo:wktLiteral"); - } - - @Test - public void emptyGeometryRaisesExpressionErrorAndLeavesBindUnbound() { - assertThrows(ExprEvalException.class, - () -> function.exec(NodeValue.makeNode("POINT Z EMPTY", WKTDatatype.INSTANCE))); - assertUnbound("'POINT Z EMPTY'^^geo:wktLiteral"); - } - - @Test - public void layoutsWithoutZStillHaveXYExtrema() { - for (String wkt : new String[] { "POINT (1 2)", "POINT M (1 2 99)" }) { - if (name.endsWith("Z")) { - assertThrows(wkt, ExprEvalException.class, - () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); - assertUnbound("'" + wkt + "'^^geo:wktLiteral"); - } else { - double expected = name.endsWith("X") ? 1 : 2; - assertEquals(wkt, NodeValue.makeDouble(expected).asNode(), - evaluate("geof:" + name + "('" + wkt + "'^^geo:wktLiteral)")); - } - } - } - - @Test - public void multiPointExtremaDistinguishZFromMeasures() { - for (boolean hasZ : new boolean[] { false, true }) { - String wkt = hasZ ? "MULTIPOINT ZM ((1 2 -4 10), (3 4 9 20))" - : "MULTIPOINT M ((1 2 10), (3 4 20))"; - if (name.endsWith("Z") && !hasZ) { - assertThrows(ExprEvalException.class, - () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); - assertUnbound("'" + wkt + "'^^geo:wktLiteral"); - } else { - double value = switch (name) { - case "minX" -> 1; - case "maxX" -> 3; - case "minY" -> 2; - case "maxY" -> 4; - case "minZ" -> -4; - case "maxZ" -> 9; - default -> throw new AssertionError(name); - }; - NodeValue expected = NodeValue.makeDouble(value); - assertEquals(expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); - assertEquals(expected.asNode(), evaluate("geof:" + name + "('" + wkt + "'^^geo:wktLiteral)")); - } - } - } - - @Test - public void mixedCollectionExtremaDoNotDependOnMemberOrder() { - for (String wkt : new String[] { - "GEOMETRYCOLLECTION (POINT Z (1 2 3))", - "GEOMETRYCOLLECTION (POINT (1 2), POINT M (1 2 999), POINT Z (1 2 3))", - "GEOMETRYCOLLECTION (POINT Z (1 2 3), POINT M (1 2 999), POINT (1 2))" }) { - NodeValue expected = NodeValue.makeDouble(name.endsWith("X") ? 1 : name.endsWith("Y") ? 2 : 3); - assertEquals(expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); - assertEquals(expected.asNode(), evaluate("geof:" + name + "('" + wkt + "'^^geo:wktLiteral)")); - } - } - - @Test - public void gmlExtremaFollowSrsDimensionOrder() { - String point = """ - ' - 10 100 -4 - '^^geo:gmlLiteral - """.replace("\n", " "); - double expected = name.endsWith("X") ? 10 : name.endsWith("Y") ? 100 : -4; - assertEquals(NodeValue.makeDouble(expected).asNode(), evaluate("geof:" + name + "(" + point + ")")); - } - - private static Node evaluate(String expression) { - String query = """ - PREFIX geof: - PREFIX geo: - SELECT ?result WHERE { BIND(%s AS ?result) } - """.formatted(expression); - try (QueryExecution execution = QueryExecution.create(query, ModelFactory.createDefaultModel())) { - ResultSet results = execution.execSelect(); - assertTrue("Expected one solution for " + expression, results.hasNext()); - QuerySolution solution = results.next(); - assertFalse("Expected only one solution for " + expression, results.hasNext()); - return solution.contains("result") ? solution.get("result").asNode() : null; - } - } - - private void assertUnbound(String argument) { - String expression = "geof:" + name + "(" + argument + ")"; - assertNull(expression, evaluate(expression)); - } -} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryMetadataErrorsTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryMetadataErrorsTest.java deleted file mode 100644 index f4f78eb7266..00000000000 --- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryMetadataErrorsTest.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * https://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. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; -import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; -import org.apache.jena.graph.Node; -import org.apache.jena.graph.NodeFactory; -import org.apache.jena.query.QueryBuildException; -import org.apache.jena.query.QueryExecution; -import org.apache.jena.query.QuerySolution; -import org.apache.jena.query.ResultSet; -import org.apache.jena.rdf.model.ModelFactory; -import org.apache.jena.sparql.expr.ExprEvalException; -import org.apache.jena.sparql.expr.NodeValue; -import org.apache.jena.sparql.function.FunctionBase1; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -@RunWith(Parameterized.class) -public class GeometryMetadataErrorsTest { - @Parameterized.Parameters(name = "function: {0}") - public static List functions() { - return List.of( - new Object[] { "is3D", new Is3DFF() }, - new Object[] { "isMeasured", new IsMeasuredFF() }, - new Object[] { "numGeometries", new NumGeometriesFF() } - ); - } - - private final String name; - private final FunctionBase1 function; - - public GeometryMetadataErrorsTest(String name, FunctionBase1 function) { - this.name = name; - this.function = function; - } - - @BeforeClass - public static void setup() { - GeoSPARQLConfig.setupNoIndex(); - } - - @Test - public void iriArgumentRaisesExpressionError() { - assertThrows(ExprEvalException.class, () -> function.exec(NodeValue.makeNode(NodeFactory.createURI("urn:not-a-literal")))); - } - - @Test - public void numericArgumentRaisesExpressionError() { - assertThrows(ExprEvalException.class, () -> function.exec(NodeValue.makeInteger(42))); - } - - @Test - public void stringWithoutGeometryDatatypeRaisesExpressionError() { - assertThrows(ExprEvalException.class, () -> function.exec(NodeValue.makeString("POINT (1 2)"))); - } - - @Test - public void malformedWktRaisesExpressionError() { - NodeValue malformed = NodeValue.makeNode("invalid", WKTDatatype.INSTANCE); - assertThrows(ExprEvalException.class, () -> function.exec(malformed)); - } - - @Test - public void iriArgumentLeavesBindUnbound() { - assertUnbound(""); - } - - @Test - public void numericArgumentLeavesBindUnbound() { - assertUnbound("42"); - } - - @Test - public void stringWithoutGeometryDatatypeLeavesBindUnbound() { - assertUnbound("'POINT (1 2)'"); - } - - @Test - public void malformedWktLeavesBindUnbound() { - assertUnbound("'invalid'^^geo:wktLiteral"); - } - - @Test - public void unboundArgumentLeavesBindUnbound() { - assertUnbound("?missing"); - } - - @Test - public void missingArgumentIsRejectedAtQueryBuild() { - assertThrows(QueryBuildException.class, () -> evaluate("geof:" + name + "()")); - } - - @Test - public void extraArgumentIsRejectedAtQueryBuild() { - assertThrows(QueryBuildException.class, - () -> evaluate("geof:" + name + "('POINT EMPTY'^^geo:wktLiteral, 1)")); - } - - private static Node evaluate(String expression) { - String query = """ - PREFIX geof: - PREFIX geo: - SELECT ?result WHERE { BIND(%s AS ?result) } - """.formatted(expression); - try (QueryExecution execution = QueryExecution.create(query, ModelFactory.createDefaultModel())) { - ResultSet results = execution.execSelect(); - assertTrue("Expected one solution for " + expression, results.hasNext()); - QuerySolution solution = results.next(); - assertFalse("Expected only one solution for " + expression, results.hasNext()); - return solution.contains("result") ? solution.get("result").asNode() : null; - } - } - - private void assertUnbound(String argument) { - String expression = "geof:" + name + "(" + argument + ")"; - assertNull(expression, evaluate(expression)); - } -} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryMetadataFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryMetadataFFTest.java deleted file mode 100644 index e5c3b7d5487..00000000000 --- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryMetadataFFTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 - * - * https://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. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.apache.jena.datatypes.xsd.XSDDatatype; -import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; -import org.apache.jena.graph.Node; -import org.apache.jena.graph.NodeFactory; -import org.apache.jena.query.QueryExecution; -import org.apache.jena.query.QuerySolution; -import org.apache.jena.query.ResultSet; -import org.apache.jena.rdf.model.ModelFactory; -import org.junit.BeforeClass; -import org.junit.Test; - -public class GeometryMetadataFFTest { - private static final String PREFIXES = """ - PREFIX geof: - PREFIX geo: - """; - - @BeforeClass - public static void setup() { - GeoSPARQLConfig.setupNoIndex(); - } - - @Test - public void is3DDistinguishesZFromM() { - assertResult("geof:is3D('POINT Z (1 2 3)'^^geo:wktLiteral)", "true", XSDDatatype.XSDboolean); - assertResult("geof:is3D('POINT M (1 2 3)'^^geo:wktLiteral)", "false", XSDDatatype.XSDboolean); - } - - @Test - public void isMeasuredDistinguishesMFromZ() { - assertResult("geof:isMeasured('POINT M (1 2 3)'^^geo:wktLiteral)", "true", XSDDatatype.XSDboolean); - assertResult("geof:isMeasured('POINT Z (1 2 3)'^^geo:wktLiteral)", "false", XSDDatatype.XSDboolean); - } - - @Test - public void emptyPointsRetainZAndMFlags() { - assertFlags("POINT EMPTY", false, false); - assertFlags("POINT Z EMPTY", true, false); - assertFlags("POINT M EMPTY", false, true); - assertFlags("POINT ZM EMPTY", true, true); - } - - @Test - public void emptyCollectionsRetainDeclaredZAndMFlags() { - assertFlags("GEOMETRYCOLLECTION EMPTY", false, false); - assertFlags("GEOMETRYCOLLECTION Z EMPTY", true, false); - assertFlags("GEOMETRYCOLLECTION M EMPTY", false, true); - assertFlags("GEOMETRYCOLLECTION ZM EMPTY", true, true); - } - - @Test - public void permissiveWktCollectionLayoutDoesNotAggregateMemberLayouts() { - // Mixed member layouts are a Jena WKT extension, not an SFA conformance case. - assertFlags("GEOMETRYCOLLECTION (POINT Z (1 2 3), POINT M (4 5 6))", false, false); - } - - @Test - public void numGeometriesCountsMembersAsAnInteger() { - assertResult("geof:numGeometries('MULTIPOINT ((1 2), (3 4))'^^geo:wktLiteral)", - "2", XSDDatatype.XSDinteger); - } - - @Test - public void numGeometriesDistinguishesEmptyAtomsFromCollections() { - assertResult("geof:numGeometries('POINT EMPTY'^^geo:wktLiteral)", "1", XSDDatatype.XSDinteger); - assertResult("geof:numGeometries('GEOMETRYCOLLECTION EMPTY'^^geo:wktLiteral)", "0", XSDDatatype.XSDinteger); - assertResult("geof:numGeometries('MULTIPOINT EMPTY'^^geo:wktLiteral)", "0", XSDDatatype.XSDinteger); - assertResult("geof:numGeometries('GEOMETRYCOLLECTION (POINT EMPTY)'^^geo:wktLiteral)", - "1", XSDDatatype.XSDinteger); - } - - @Test - public void gmlPointUsesMetadataFunctions() { - String point = """ - ' - 1 2 - '^^geo:gmlLiteral - """.replace("\n", " "); - assertResult("geof:numGeometries(" + point + ")", "1", XSDDatatype.XSDinteger); - assertResult("geof:is3D(" + point + ")", "false", XSDDatatype.XSDboolean); - assertResult("geof:isMeasured(" + point + ")", "false", XSDDatatype.XSDboolean); - assertResult("geof:isEmpty(" + point + ")", "false", XSDDatatype.XSDboolean); - } - - @Test - public void threeDimensionalGmlPointHasZButNoMeasure() { - String point = """ - ' - 2 1 3 - '^^geo:gmlLiteral - """.replace("\n", " "); - assertResult("geof:is3D(" + point + ")", "true", XSDDatatype.XSDboolean); - assertResult("geof:isMeasured(" + point + ")", "false", XSDDatatype.XSDboolean); - assertResult("geof:isEmpty(" + point + ")", "false", XSDDatatype.XSDboolean); - } - - private static void assertFlags(String wkt, boolean hasZ, boolean hasM) { - assertResult("geof:is3D('" + wkt + "'^^geo:wktLiteral)", Boolean.toString(hasZ), XSDDatatype.XSDboolean); - assertResult("geof:isMeasured('" + wkt + "'^^geo:wktLiteral)", Boolean.toString(hasM), XSDDatatype.XSDboolean); - } - - private static void assertResult(String expression, String lexicalForm, XSDDatatype datatype) { - Node expected = NodeFactory.createLiteralDT(lexicalForm, datatype); - assertEquals(expression, expected, evaluate(expression)); - } - - private static Node evaluate(String expression) { - String query = PREFIXES + "SELECT ?result WHERE { BIND(" + expression + " AS ?result) }"; - try (QueryExecution execution = QueryExecution.create(query, ModelFactory.createDefaultModel())) { - ResultSet results = execution.execSelect(); - assertTrue("Expected one solution for " + expression, results.hasNext()); - QuerySolution solution = results.next(); - assertFalse("Expected only one solution for " + expression, results.hasNext()); - return solution.contains("result") ? solution.get("result").asNode() : null; - } - } -} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryPropertyFFTestSupport.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryPropertyFFTestSupport.java new file mode 100644 index 00000000000..e8346cb1409 --- /dev/null +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/GeometryPropertyFFTestSupport.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; + +import org.apache.jena.graph.Node; +import org.apache.jena.query.QueryExecution; +import org.apache.jena.query.QuerySolution; +import org.apache.jena.query.ResultSet; +import org.apache.jena.rdf.model.ModelFactory; + +final class GeometryPropertyFFTestSupport { + static final String GML_2D_POINT = """ + ' + 1 2 + '^^geo:gmlLiteral + """.replace("\n", " "); + + static final String GML_3D_POINT = """ + ' + 2 1 3 + '^^geo:gmlLiteral + """.replace("\n", " "); + + private GeometryPropertyFFTestSupport() {} + + static String wktArgument(String wkt) { + return "'" + wkt + "'^^geo:wktLiteral"; + } + + static Node evaluate(String expression) { + String query = """ + PREFIX geof: + PREFIX geo: + SELECT ?result WHERE { BIND(%s AS ?result) } + """.formatted(expression); + try (QueryExecution execution = QueryExecution.create(query, ModelFactory.createDefaultModel())) { + ResultSet results = execution.execSelect(); + QuerySolution solution = results.next(); + if (results.hasNext()) + throw new IllegalStateException("Expected one solution for " + expression); + return solution.contains("result") ? solution.get("result").asNode() : null; + } + } +} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/Is3DFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/Is3DFFTest.java new file mode 100644 index 00000000000..b4f302f83d0 --- /dev/null +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/Is3DFFTest.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; + +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_2D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_3D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.evaluate; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.wktArgument; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; +import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.sparql.expr.ExprEvalException; +import org.apache.jena.sparql.expr.NodeValue; +import org.junit.BeforeClass; +import org.junit.Test; + +public class Is3DFFTest { + private final Is3DFF function = new Is3DFF(); + + @BeforeClass + public static void setup() { + GeoSPARQLConfig.setupNoIndex(); + } + + @Test + public void directExecutionAcceptsExplicitWktCrs() { + NodeValue geometry = NodeValue.makeNode( + " POINT Z (1 2 3)", WKTDatatype.INSTANCE); + assertEquals(NodeValue.makeBoolean(true), function.exec(geometry)); + } + + @Test + public void distinguishesZFromM() { + assertResult("POINT Z (1 2 3)", true); + assertResult("POINT M (1 2 3)", false); + } + + @Test + public void emptyPointsRetainDeclaredLayout() { + assertResult("POINT EMPTY", false); + assertResult("POINT Z EMPTY", true); + assertResult("POINT M EMPTY", false); + assertResult("POINT ZM EMPTY", true); + } + + @Test + public void emptyCollectionsRetainDeclaredLayout() { + assertResult("GEOMETRYCOLLECTION EMPTY", false); + assertResult("GEOMETRYCOLLECTION Z EMPTY", true); + assertResult("GEOMETRYCOLLECTION M EMPTY", false); + assertResult("GEOMETRYCOLLECTION ZM EMPTY", true); + } + + @Test + public void permissiveWktCollectionDoesNotAggregateMemberLayouts() { + // Mixed member layouts are a Jena WKT extension, not an SFA conformance case. + assertResult("GEOMETRYCOLLECTION (POINT Z (1 2 3), POINT M (4 5 6))", false); + } + + @Test + public void gmlPointsReportTheirZLayout() { + assertEquals(NodeValue.makeBoolean(false).asNode(), evaluate("geof:is3D(" + GML_2D_POINT + ")")); + assertEquals(NodeValue.makeBoolean(true).asNode(), evaluate("geof:is3D(" + GML_3D_POINT + ")")); + } + + @Test + public void invalidArgumentsRaiseExpressionErrors() { + for (NodeValue value : new NodeValue[] { + NodeValue.makeNode(NodeFactory.createURI("urn:not-a-literal")), + NodeValue.makeInteger(42), + NodeValue.makeString("POINT (1 2)"), + NodeValue.makeNode("invalid", WKTDatatype.INSTANCE) }) { + assertThrows(ExprEvalException.class, () -> function.exec(value)); + } + } + + private static void assertResult(String wkt, boolean expected) { + assertEquals(wkt, NodeValue.makeBoolean(expected).asNode(), + evaluate("geof:is3D(" + wktArgument(wkt) + ")")); + } +} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/IsEmptyFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/IsEmptyFFTest.java index fbd7e715f56..817129e8b47 100644 --- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/IsEmptyFFTest.java +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/IsEmptyFFTest.java @@ -20,6 +20,11 @@ */ package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_2D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_3D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.evaluate; + +import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; import org.apache.jena.sparql.expr.NodeValue; import org.junit.After; @@ -40,6 +45,7 @@ public IsEmptyFFTest() { @BeforeClass public static void setUpClass() { + GeoSPARQLConfig.setupNoIndex(); } @AfterClass @@ -80,4 +86,10 @@ public void testExec_true() { assertEquals(expResult, result); } + @Test + public void gmlPointsAreNotEmpty() { + assertEquals(NodeValue.makeBoolean(false).asNode(), evaluate("geof:isEmpty(" + GML_2D_POINT + ")")); + assertEquals(NodeValue.makeBoolean(false).asNode(), evaluate("geof:isEmpty(" + GML_3D_POINT + ")")); + } + } diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/IsMeasuredFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/IsMeasuredFFTest.java new file mode 100644 index 00000000000..fe776c0c4cd --- /dev/null +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/IsMeasuredFFTest.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; + +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_2D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_3D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.evaluate; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.wktArgument; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; +import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.sparql.expr.ExprEvalException; +import org.apache.jena.sparql.expr.NodeValue; +import org.junit.BeforeClass; +import org.junit.Test; + +public class IsMeasuredFFTest { + private final IsMeasuredFF function = new IsMeasuredFF(); + + @BeforeClass + public static void setup() { + GeoSPARQLConfig.setupNoIndex(); + } + + @Test + public void directExecutionAcceptsExplicitWktCrs() { + NodeValue geometry = NodeValue.makeNode( + " POINT M (1 2 3)", WKTDatatype.INSTANCE); + assertEquals(NodeValue.makeBoolean(true), function.exec(geometry)); + } + + @Test + public void distinguishesMFromZ() { + assertResult("POINT M (1 2 3)", true); + assertResult("POINT Z (1 2 3)", false); + } + + @Test + public void emptyPointsRetainDeclaredLayout() { + assertResult("POINT EMPTY", false); + assertResult("POINT Z EMPTY", false); + assertResult("POINT M EMPTY", true); + assertResult("POINT ZM EMPTY", true); + } + + @Test + public void emptyCollectionsRetainDeclaredLayout() { + assertResult("GEOMETRYCOLLECTION EMPTY", false); + assertResult("GEOMETRYCOLLECTION Z EMPTY", false); + assertResult("GEOMETRYCOLLECTION M EMPTY", true); + assertResult("GEOMETRYCOLLECTION ZM EMPTY", true); + } + + @Test + public void permissiveWktCollectionDoesNotAggregateMemberLayouts() { + // Mixed member layouts are a Jena WKT extension, not an SFA conformance case. + assertResult("GEOMETRYCOLLECTION (POINT Z (1 2 3), POINT M (4 5 6))", false); + } + + @Test + public void gmlPointsAreNotMeasured() { + assertEquals(NodeValue.makeBoolean(false).asNode(), evaluate("geof:isMeasured(" + GML_2D_POINT + ")")); + assertEquals(NodeValue.makeBoolean(false).asNode(), evaluate("geof:isMeasured(" + GML_3D_POINT + ")")); + } + + @Test + public void invalidArgumentsRaiseExpressionErrors() { + for (NodeValue value : new NodeValue[] { + NodeValue.makeNode(NodeFactory.createURI("urn:not-a-literal")), + NodeValue.makeInteger(42), + NodeValue.makeString("POINT (1 2)"), + NodeValue.makeNode("invalid", WKTDatatype.INSTANCE) }) { + assertThrows(ExprEvalException.class, () -> function.exec(value)); + } + } + + private static void assertResult(String wkt, boolean expected) { + assertEquals(wkt, NodeValue.makeBoolean(expected).asNode(), + evaluate("geof:isMeasured(" + wktArgument(wkt) + ")")); + } +} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MaxXFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MaxXFFTest.java new file mode 100644 index 00000000000..2596d771a45 --- /dev/null +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MaxXFFTest.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; + +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_3D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.evaluate; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.wktArgument; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; +import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.sparql.expr.ExprEvalException; +import org.apache.jena.sparql.expr.NodeValue; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MaxXFFTest { + private final MaxXFF function = new MaxXFF(); + + @BeforeClass + public static void setup() { + GeoSPARQLConfig.setupNoIndex(); + } + + @Test + public void directExecutionAcceptsExplicitWktCrs() { + NodeValue geometry = NodeValue.makeNode( + " LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)", + WKTDatatype.INSTANCE); + assertEquals(NodeValue.makeDouble(-2), function.exec(geometry)); + } + + @Test + public void returnsDoubleLiteralThroughSparql() { + String wkt = "LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)"; + assertEquals(NodeValue.makeDouble(-2).asNode(), + evaluate("geof:maxX(" + wktArgument(wkt) + ")")); + } + + @Test + public void followsSrsDimensionOrder() { + String wkt = " LINESTRING (10 100, 20 120)"; + assertEquals(NodeValue.makeDouble(20).asNode(), + evaluate("geof:maxX(" + wktArgument(wkt) + ")")); + } + + @Test + public void infiniteOrdinateRaisesExpressionError() { + for (String infinity : new String[] { "Infinity", "-Infinity" }) { + String wkt = "LINESTRING Z (-4 -4 -4, " + infinity + " 1 1, 9 9 9)"; + assertThrows(wkt, ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void nanOrdinateRaisesExpressionError() { + String wkt = "LINESTRING (1 1, NaN 2, 3 3)"; + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + + @Test + public void emptyGeometryRaisesExpressionError() { + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode("POINT Z EMPTY", WKTDatatype.INSTANCE))); + } + + @Test + public void layoutsWithoutZHaveTheExpectedResult() { + for (String wkt : new String[] { "POINT (1 2)", "POINT M (1 2 99)" }) { + assertEquals(wkt, NodeValue.makeDouble(1), + function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void multiPointExtremaDistinguishZFromMeasures() { + for (String wkt : new String[] { + "MULTIPOINT M ((1 2 10), (3 4 20))", + "MULTIPOINT ZM ((1 2 -4 10), (3 4 9 20))" }) { + NodeValue expected = NodeValue.makeDouble(3); + assertEquals(wkt, expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(wkt, expected.asNode(), evaluate("geof:maxX(" + wktArgument(wkt) + ")")); + } + } + + @Test + public void mixedCollectionExtremaDoNotDependOnMemberOrder() { + for (String wkt : new String[] { + "GEOMETRYCOLLECTION (POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT (1 2), POINT M (1 2 999), POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT Z (1 2 3), POINT M (1 2 999), POINT (1 2))" }) { + NodeValue expected = NodeValue.makeDouble(1); + assertEquals(wkt, expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(wkt, expected.asNode(), evaluate("geof:maxX(" + wktArgument(wkt) + ")")); + } + } + + @Test + public void gmlExtremaFollowSrsDimensionOrder() { + assertEquals(NodeValue.makeDouble(2).asNode(), + evaluate("geof:maxX(" + GML_3D_POINT + ")")); + } + + @Test + public void invalidArgumentsRaiseExpressionErrors() { + for (NodeValue value : new NodeValue[] { + NodeValue.makeNode(NodeFactory.createURI("urn:not-a-literal")), + NodeValue.makeInteger(42), + NodeValue.makeString("POINT (1 2)"), + NodeValue.makeNode("invalid", WKTDatatype.INSTANCE) }) { + assertThrows(ExprEvalException.class, () -> function.exec(value)); + } + } +} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MaxYFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MaxYFFTest.java new file mode 100644 index 00000000000..dc7f204efd6 --- /dev/null +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MaxYFFTest.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; + +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_3D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.evaluate; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.wktArgument; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; +import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.sparql.expr.ExprEvalException; +import org.apache.jena.sparql.expr.NodeValue; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MaxYFFTest { + private final MaxYFF function = new MaxYFF(); + + @BeforeClass + public static void setup() { + GeoSPARQLConfig.setupNoIndex(); + } + + @Test + public void directExecutionAcceptsExplicitWktCrs() { + NodeValue geometry = NodeValue.makeNode( + " LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)", + WKTDatatype.INSTANCE); + assertEquals(NodeValue.makeDouble(8), function.exec(geometry)); + } + + @Test + public void returnsDoubleLiteralThroughSparql() { + String wkt = "LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)"; + assertEquals(NodeValue.makeDouble(8).asNode(), + evaluate("geof:maxY(" + wktArgument(wkt) + ")")); + } + + @Test + public void followsSrsDimensionOrder() { + String wkt = " LINESTRING (10 100, 20 120)"; + assertEquals(NodeValue.makeDouble(120).asNode(), + evaluate("geof:maxY(" + wktArgument(wkt) + ")")); + } + + @Test + public void infiniteOrdinateRaisesExpressionError() { + for (String infinity : new String[] { "Infinity", "-Infinity" }) { + String wkt = "LINESTRING Z (-4 -4 -4, " + "1 " + infinity + " 1, 9 9 9)"; + assertThrows(wkt, ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void nanOrdinateRaisesExpressionError() { + String wkt = "LINESTRING (1 1, 2 NaN, 3 3)"; + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + + @Test + public void emptyGeometryRaisesExpressionError() { + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode("POINT Z EMPTY", WKTDatatype.INSTANCE))); + } + + @Test + public void layoutsWithoutZHaveTheExpectedResult() { + for (String wkt : new String[] { "POINT (1 2)", "POINT M (1 2 99)" }) { + assertEquals(wkt, NodeValue.makeDouble(2), + function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void multiPointExtremaDistinguishZFromMeasures() { + for (String wkt : new String[] { + "MULTIPOINT M ((1 2 10), (3 4 20))", + "MULTIPOINT ZM ((1 2 -4 10), (3 4 9 20))" }) { + NodeValue expected = NodeValue.makeDouble(4); + assertEquals(wkt, expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(wkt, expected.asNode(), evaluate("geof:maxY(" + wktArgument(wkt) + ")")); + } + } + + @Test + public void mixedCollectionExtremaDoNotDependOnMemberOrder() { + for (String wkt : new String[] { + "GEOMETRYCOLLECTION (POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT (1 2), POINT M (1 2 999), POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT Z (1 2 3), POINT M (1 2 999), POINT (1 2))" }) { + NodeValue expected = NodeValue.makeDouble(2); + assertEquals(wkt, expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(wkt, expected.asNode(), evaluate("geof:maxY(" + wktArgument(wkt) + ")")); + } + } + + @Test + public void gmlExtremaFollowSrsDimensionOrder() { + assertEquals(NodeValue.makeDouble(1).asNode(), + evaluate("geof:maxY(" + GML_3D_POINT + ")")); + } + + @Test + public void invalidArgumentsRaiseExpressionErrors() { + for (NodeValue value : new NodeValue[] { + NodeValue.makeNode(NodeFactory.createURI("urn:not-a-literal")), + NodeValue.makeInteger(42), + NodeValue.makeString("POINT (1 2)"), + NodeValue.makeNode("invalid", WKTDatatype.INSTANCE) }) { + assertThrows(ExprEvalException.class, () -> function.exec(value)); + } + } +} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MaxZFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MaxZFFTest.java new file mode 100644 index 00000000000..8a8162726b7 --- /dev/null +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MaxZFFTest.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; + +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_3D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.evaluate; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.wktArgument; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; +import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.sparql.expr.ExprEvalException; +import org.apache.jena.sparql.expr.NodeValue; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MaxZFFTest { + private final MaxZFF function = new MaxZFF(); + + @BeforeClass + public static void setup() { + GeoSPARQLConfig.setupNoIndex(); + } + + @Test + public void directExecutionAcceptsExplicitWktCrs() { + NodeValue geometry = NodeValue.makeNode( + " LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)", + WKTDatatype.INSTANCE); + assertEquals(NodeValue.makeDouble(-1), function.exec(geometry)); + } + + @Test + public void returnsDoubleLiteralThroughSparql() { + String wkt = "LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)"; + assertEquals(NodeValue.makeDouble(-1).asNode(), + evaluate("geof:maxZ(" + wktArgument(wkt) + ")")); + } + + @Test + public void followsSrsDimensionOrder() { + String wkt = " LINESTRING Z (10 100 -4, 20 120 9)"; + assertEquals(NodeValue.makeDouble(9).asNode(), + evaluate("geof:maxZ(" + wktArgument(wkt) + ")")); + assertThrows(ExprEvalException.class, () -> function.exec(NodeValue.makeNode( + " LINESTRING (10 100, 20 120)", WKTDatatype.INSTANCE))); + } + + @Test + public void infiniteOrdinateRaisesExpressionError() { + for (String infinity : new String[] { "Infinity", "-Infinity" }) { + String wkt = "LINESTRING Z (-4 -4 -4, " + "1 1 " + infinity + ", 9 9 9)"; + assertThrows(wkt, ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void emptyGeometryRaisesExpressionError() { + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode("POINT Z EMPTY", WKTDatatype.INSTANCE))); + } + + @Test + public void layoutsWithoutZHaveTheExpectedResult() { + for (String wkt : new String[] { "POINT (1 2)", "POINT M (1 2 99)" }) { + assertThrows(wkt, ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void multiPointExtremaDistinguishZFromMeasures() { + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode("MULTIPOINT M ((1 2 10), (3 4 20))", WKTDatatype.INSTANCE))); + String wkt = "MULTIPOINT ZM ((1 2 -4 10), (3 4 9 20))"; + NodeValue expected = NodeValue.makeDouble(9); + assertEquals(expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(expected.asNode(), evaluate("geof:maxZ(" + wktArgument(wkt) + ")")); + } + + @Test + public void mixedCollectionExtremaDoNotDependOnMemberOrder() { + for (String wkt : new String[] { + "GEOMETRYCOLLECTION (POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT (1 2), POINT M (1 2 999), POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT Z (1 2 3), POINT M (1 2 999), POINT (1 2))" }) { + NodeValue expected = NodeValue.makeDouble(3); + assertEquals(wkt, expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(wkt, expected.asNode(), evaluate("geof:maxZ(" + wktArgument(wkt) + ")")); + } + } + + @Test + public void gmlExtremaFollowSrsDimensionOrder() { + assertEquals(NodeValue.makeDouble(3).asNode(), + evaluate("geof:maxZ(" + GML_3D_POINT + ")")); + } + + @Test + public void invalidArgumentsRaiseExpressionErrors() { + for (NodeValue value : new NodeValue[] { + NodeValue.makeNode(NodeFactory.createURI("urn:not-a-literal")), + NodeValue.makeInteger(42), + NodeValue.makeString("POINT (1 2)"), + NodeValue.makeNode("invalid", WKTDatatype.INSTANCE) }) { + assertThrows(ExprEvalException.class, () -> function.exec(value)); + } + } +} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MinXFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MinXFFTest.java new file mode 100644 index 00000000000..1ee4ce8481f --- /dev/null +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MinXFFTest.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; + +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_3D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.evaluate; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.wktArgument; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; +import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.sparql.expr.ExprEvalException; +import org.apache.jena.sparql.expr.NodeValue; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MinXFFTest { + private final MinXFF function = new MinXFF(); + + @BeforeClass + public static void setup() { + GeoSPARQLConfig.setupNoIndex(); + } + + @Test + public void directExecutionAcceptsExplicitWktCrs() { + NodeValue geometry = NodeValue.makeNode( + " LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)", + WKTDatatype.INSTANCE); + assertEquals(NodeValue.makeDouble(-9), function.exec(geometry)); + } + + @Test + public void returnsDoubleLiteralThroughSparql() { + String wkt = "LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)"; + assertEquals(NodeValue.makeDouble(-9).asNode(), + evaluate("geof:minX(" + wktArgument(wkt) + ")")); + } + + @Test + public void followsSrsDimensionOrder() { + String wkt = " LINESTRING (10 100, 20 120)"; + assertEquals(NodeValue.makeDouble(10).asNode(), + evaluate("geof:minX(" + wktArgument(wkt) + ")")); + } + + @Test + public void infiniteOrdinateRaisesExpressionError() { + for (String infinity : new String[] { "Infinity", "-Infinity" }) { + String wkt = "LINESTRING Z (-4 -4 -4, " + infinity + " 1 1, 9 9 9)"; + assertThrows(wkt, ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void nanOrdinateRaisesExpressionError() { + String wkt = "LINESTRING (1 1, NaN 2, 3 3)"; + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + + @Test + public void emptyGeometryRaisesExpressionError() { + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode("POINT Z EMPTY", WKTDatatype.INSTANCE))); + } + + @Test + public void layoutsWithoutZHaveTheExpectedResult() { + for (String wkt : new String[] { "POINT (1 2)", "POINT M (1 2 99)" }) { + assertEquals(wkt, NodeValue.makeDouble(1), + function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void multiPointExtremaDistinguishZFromMeasures() { + for (String wkt : new String[] { + "MULTIPOINT M ((1 2 10), (3 4 20))", + "MULTIPOINT ZM ((1 2 -4 10), (3 4 9 20))" }) { + NodeValue expected = NodeValue.makeDouble(1); + assertEquals(wkt, expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(wkt, expected.asNode(), evaluate("geof:minX(" + wktArgument(wkt) + ")")); + } + } + + @Test + public void mixedCollectionExtremaDoNotDependOnMemberOrder() { + for (String wkt : new String[] { + "GEOMETRYCOLLECTION (POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT (1 2), POINT M (1 2 999), POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT Z (1 2 3), POINT M (1 2 999), POINT (1 2))" }) { + NodeValue expected = NodeValue.makeDouble(1); + assertEquals(wkt, expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(wkt, expected.asNode(), evaluate("geof:minX(" + wktArgument(wkt) + ")")); + } + } + + @Test + public void gmlExtremaFollowSrsDimensionOrder() { + assertEquals(NodeValue.makeDouble(2).asNode(), + evaluate("geof:minX(" + GML_3D_POINT + ")")); + } + + @Test + public void invalidArgumentsRaiseExpressionErrors() { + for (NodeValue value : new NodeValue[] { + NodeValue.makeNode(NodeFactory.createURI("urn:not-a-literal")), + NodeValue.makeInteger(42), + NodeValue.makeString("POINT (1 2)"), + NodeValue.makeNode("invalid", WKTDatatype.INSTANCE) }) { + assertThrows(ExprEvalException.class, () -> function.exec(value)); + } + } +} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MinYFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MinYFFTest.java new file mode 100644 index 00000000000..1842420c7c1 --- /dev/null +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MinYFFTest.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; + +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_3D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.evaluate; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.wktArgument; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; +import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.sparql.expr.ExprEvalException; +import org.apache.jena.sparql.expr.NodeValue; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MinYFFTest { + private final MinYFF function = new MinYFF(); + + @BeforeClass + public static void setup() { + GeoSPARQLConfig.setupNoIndex(); + } + + @Test + public void directExecutionAcceptsExplicitWktCrs() { + NodeValue geometry = NodeValue.makeNode( + " LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)", + WKTDatatype.INSTANCE); + assertEquals(NodeValue.makeDouble(3), function.exec(geometry)); + } + + @Test + public void returnsDoubleLiteralThroughSparql() { + String wkt = "LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)"; + assertEquals(NodeValue.makeDouble(3).asNode(), + evaluate("geof:minY(" + wktArgument(wkt) + ")")); + } + + @Test + public void followsSrsDimensionOrder() { + String wkt = " LINESTRING (10 100, 20 120)"; + assertEquals(NodeValue.makeDouble(100).asNode(), + evaluate("geof:minY(" + wktArgument(wkt) + ")")); + } + + @Test + public void infiniteOrdinateRaisesExpressionError() { + for (String infinity : new String[] { "Infinity", "-Infinity" }) { + String wkt = "LINESTRING Z (-4 -4 -4, " + "1 " + infinity + " 1, 9 9 9)"; + assertThrows(wkt, ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void nanOrdinateRaisesExpressionError() { + String wkt = "LINESTRING (1 1, 2 NaN, 3 3)"; + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + + @Test + public void emptyGeometryRaisesExpressionError() { + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode("POINT Z EMPTY", WKTDatatype.INSTANCE))); + } + + @Test + public void layoutsWithoutZHaveTheExpectedResult() { + for (String wkt : new String[] { "POINT (1 2)", "POINT M (1 2 99)" }) { + assertEquals(wkt, NodeValue.makeDouble(2), + function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void multiPointExtremaDistinguishZFromMeasures() { + for (String wkt : new String[] { + "MULTIPOINT M ((1 2 10), (3 4 20))", + "MULTIPOINT ZM ((1 2 -4 10), (3 4 9 20))" }) { + NodeValue expected = NodeValue.makeDouble(2); + assertEquals(wkt, expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(wkt, expected.asNode(), evaluate("geof:minY(" + wktArgument(wkt) + ")")); + } + } + + @Test + public void mixedCollectionExtremaDoNotDependOnMemberOrder() { + for (String wkt : new String[] { + "GEOMETRYCOLLECTION (POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT (1 2), POINT M (1 2 999), POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT Z (1 2 3), POINT M (1 2 999), POINT (1 2))" }) { + NodeValue expected = NodeValue.makeDouble(2); + assertEquals(wkt, expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(wkt, expected.asNode(), evaluate("geof:minY(" + wktArgument(wkt) + ")")); + } + } + + @Test + public void gmlExtremaFollowSrsDimensionOrder() { + assertEquals(NodeValue.makeDouble(1).asNode(), + evaluate("geof:minY(" + GML_3D_POINT + ")")); + } + + @Test + public void invalidArgumentsRaiseExpressionErrors() { + for (NodeValue value : new NodeValue[] { + NodeValue.makeNode(NodeFactory.createURI("urn:not-a-literal")), + NodeValue.makeInteger(42), + NodeValue.makeString("POINT (1 2)"), + NodeValue.makeNode("invalid", WKTDatatype.INSTANCE) }) { + assertThrows(ExprEvalException.class, () -> function.exec(value)); + } + } +} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MinZFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MinZFFTest.java new file mode 100644 index 00000000000..600a86bf315 --- /dev/null +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/MinZFFTest.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; + +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_3D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.evaluate; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.wktArgument; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; +import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.sparql.expr.ExprEvalException; +import org.apache.jena.sparql.expr.NodeValue; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MinZFFTest { + private final MinZFF function = new MinZFF(); + + @BeforeClass + public static void setup() { + GeoSPARQLConfig.setupNoIndex(); + } + + @Test + public void directExecutionAcceptsExplicitWktCrs() { + NodeValue geometry = NodeValue.makeNode( + " LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)", + WKTDatatype.INSTANCE); + assertEquals(NodeValue.makeDouble(-7), function.exec(geometry)); + } + + @Test + public void returnsDoubleLiteralThroughSparql() { + String wkt = "LINESTRING ZM (-9 8 -7 100, -2 3 -1 -100)"; + assertEquals(NodeValue.makeDouble(-7).asNode(), + evaluate("geof:minZ(" + wktArgument(wkt) + ")")); + } + + @Test + public void followsSrsDimensionOrder() { + String wkt = " LINESTRING Z (10 100 -4, 20 120 9)"; + assertEquals(NodeValue.makeDouble(-4).asNode(), + evaluate("geof:minZ(" + wktArgument(wkt) + ")")); + assertThrows(ExprEvalException.class, () -> function.exec(NodeValue.makeNode( + " LINESTRING (10 100, 20 120)", WKTDatatype.INSTANCE))); + } + + @Test + public void infiniteOrdinateRaisesExpressionError() { + for (String infinity : new String[] { "Infinity", "-Infinity" }) { + String wkt = "LINESTRING Z (-4 -4 -4, " + "1 1 " + infinity + ", 9 9 9)"; + assertThrows(wkt, ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void emptyGeometryRaisesExpressionError() { + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode("POINT Z EMPTY", WKTDatatype.INSTANCE))); + } + + @Test + public void layoutsWithoutZHaveTheExpectedResult() { + for (String wkt : new String[] { "POINT (1 2)", "POINT M (1 2 99)" }) { + assertThrows(wkt, ExprEvalException.class, + () -> function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + } + } + + @Test + public void multiPointExtremaDistinguishZFromMeasures() { + assertThrows(ExprEvalException.class, + () -> function.exec(NodeValue.makeNode("MULTIPOINT M ((1 2 10), (3 4 20))", WKTDatatype.INSTANCE))); + String wkt = "MULTIPOINT ZM ((1 2 -4 10), (3 4 9 20))"; + NodeValue expected = NodeValue.makeDouble(-4); + assertEquals(expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(expected.asNode(), evaluate("geof:minZ(" + wktArgument(wkt) + ")")); + } + + @Test + public void mixedCollectionExtremaDoNotDependOnMemberOrder() { + for (String wkt : new String[] { + "GEOMETRYCOLLECTION (POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT (1 2), POINT M (1 2 999), POINT Z (1 2 3))", + "GEOMETRYCOLLECTION (POINT Z (1 2 3), POINT M (1 2 999), POINT (1 2))" }) { + NodeValue expected = NodeValue.makeDouble(3); + assertEquals(wkt, expected, function.exec(NodeValue.makeNode(wkt, WKTDatatype.INSTANCE))); + assertEquals(wkt, expected.asNode(), evaluate("geof:minZ(" + wktArgument(wkt) + ")")); + } + } + + @Test + public void gmlExtremaFollowSrsDimensionOrder() { + assertEquals(NodeValue.makeDouble(3).asNode(), + evaluate("geof:minZ(" + GML_3D_POINT + ")")); + } + + @Test + public void invalidArgumentsRaiseExpressionErrors() { + for (NodeValue value : new NodeValue[] { + NodeValue.makeNode(NodeFactory.createURI("urn:not-a-literal")), + NodeValue.makeInteger(42), + NodeValue.makeString("POINT (1 2)"), + NodeValue.makeNode("invalid", WKTDatatype.INSTANCE) }) { + assertThrows(ExprEvalException.class, () -> function.exec(value)); + } + } +} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/NumGeometriesFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/NumGeometriesFFTest.java new file mode 100644 index 00000000000..1d20c45714a --- /dev/null +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/topological/filter_functions/geometry_property/NumGeometriesFFTest.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property; + +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.GML_2D_POINT; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.evaluate; +import static org.apache.jena.geosparql.geof.topological.filter_functions.geometry_property.GeometryPropertyFFTestSupport.wktArgument; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; +import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.sparql.expr.ExprEvalException; +import org.apache.jena.sparql.expr.NodeValue; +import org.junit.BeforeClass; +import org.junit.Test; + +public class NumGeometriesFFTest { + private final NumGeometriesFF function = new NumGeometriesFF(); + + @BeforeClass + public static void setup() { + GeoSPARQLConfig.setupNoIndex(); + } + + @Test + public void directExecutionAcceptsExplicitWktCrs() { + NodeValue geometry = NodeValue.makeNode( + " POINT (1 2)", WKTDatatype.INSTANCE); + assertEquals(NodeValue.makeInteger(1), function.exec(geometry)); + } + + @Test + public void countsMembersAsAnInteger() { + assertResult("MULTIPOINT ((1 2), (3 4))", 2); + } + + @Test + public void distinguishesAtomsFromCollections() { + assertResult("POINT (1 2)", 1); + assertResult("POINT EMPTY", 1); + assertResult("GEOMETRYCOLLECTION EMPTY", 0); + assertResult("MULTIPOINT EMPTY", 0); + assertResult("GEOMETRYCOLLECTION (POINT EMPTY)", 1); + } + + @Test + public void gmlPointCountsAsOne() { + assertEquals(NodeValue.makeInteger(1).asNode(), evaluate("geof:numGeometries(" + GML_2D_POINT + ")")); + } + + @Test + public void invalidArgumentsRaiseExpressionErrors() { + for (NodeValue value : new NodeValue[] { + NodeValue.makeNode(NodeFactory.createURI("urn:not-a-literal")), + NodeValue.makeInteger(42), + NodeValue.makeString("POINT (1 2)"), + NodeValue.makeNode("invalid", WKTDatatype.INSTANCE) }) { + assertThrows(ExprEvalException.class, () -> function.exec(value)); + } + } + + private static void assertResult(String wkt, int expected) { + assertEquals(wkt, NodeValue.makeInteger(expected).asNode(), + evaluate("geof:numGeometries(" + wktArgument(wkt) + ")")); + } +} diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/GeometryTransformIndexTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/index/GeometryTransformIndexTest.java similarity index 95% rename from jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/GeometryTransformIndexTest.java rename to jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/index/GeometryTransformIndexTest.java index 434f2711dc9..6ff237bd421 100644 --- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/GeometryTransformIndexTest.java +++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/index/GeometryTransformIndexTest.java @@ -18,14 +18,14 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package org.apache.jena.geosparql.implementation; +package org.apache.jena.geosparql.implementation.index; import static org.junit.Assert.assertEquals; import org.apache.jena.geosparql.configuration.GeoSPARQLConfig; +import org.apache.jena.geosparql.implementation.GeometryWrapper; import org.apache.jena.geosparql.implementation.datatype.GMLDatatype; import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; -import org.apache.jena.geosparql.implementation.index.GeometryTransformIndex; import org.junit.Test; public class GeometryTransformIndexTest {