This is an automated email from the ASF dual-hosted git repository.
andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git
The following commit(s) were added to refs/heads/main by this push:
new 119743876b GH-1327: allow geo index search for literals
119743876b is described below
commit 119743876bcfd83fb12534cbcb133eb42b7512f9
Author: Simon Bin <[email protected]>
AuthorDate: Sat Jul 30 18:13:20 2022 +0200
GH-1327: allow geo index search for literals
* Java line endings
* allow geo index search for literals
* add test for geo-literal extension
* missing null check
---
.../geo/topological/GenericPropertyFunction.java | 667 +++++++++++----------
.../topological/SpatialObjectGeometryLiteral.java | 10 +
.../topological/GenericPropertyFunctionTest.java | 33 +
3 files changed, 378 insertions(+), 332 deletions(-)
diff --git
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/GenericPropertyFunction.java
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/GenericPropertyFunction.java
index 60d1676f4e..4874dfc0ee 100644
---
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/GenericPropertyFunction.java
+++
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/GenericPropertyFunction.java
@@ -1,332 +1,335 @@
-/*
- * 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
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.jena.geosparql.geo.topological;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import org.apache.jena.geosparql.geof.topological.GenericFilterFunction;
-import org.apache.jena.geosparql.implementation.GeometryWrapper;
-import org.apache.jena.geosparql.implementation.index.QueryRewriteIndex;
-import org.apache.jena.geosparql.implementation.vocabulary.Geo;
-import org.apache.jena.geosparql.implementation.vocabulary.SpatialExtension;
-import org.apache.jena.geosparql.spatial.SpatialIndex;
-import org.apache.jena.geosparql.spatial.SpatialIndexException;
-import org.apache.jena.graph.Graph;
-import org.apache.jena.graph.Node;
-import org.apache.jena.graph.Triple;
-import org.apache.jena.rdf.model.Resource;
-import org.apache.jena.sparql.core.Var;
-import org.apache.jena.sparql.engine.ExecutionContext;
-import org.apache.jena.sparql.engine.QueryIterator;
-import org.apache.jena.sparql.engine.binding.Binding;
-import org.apache.jena.sparql.engine.binding.BindingFactory;
-import org.apache.jena.sparql.engine.iterator.QueryIterConcat;
-import org.apache.jena.sparql.engine.iterator.QueryIterNullIterator;
-import org.apache.jena.sparql.engine.iterator.QueryIterSingleton;
-import org.apache.jena.sparql.expr.ExprEvalException;
-import org.apache.jena.sparql.pfunction.PFuncSimple;
-import org.apache.jena.sparql.util.FmtUtils;
-import org.apache.jena.util.iterator.ExtendedIterator;
-import org.apache.jena.vocabulary.RDF;
-import org.locationtech.jts.geom.Envelope;
-import org.opengis.geometry.MismatchedDimensionException;
-import org.opengis.referencing.operation.TransformException;
-import org.opengis.util.FactoryException;
-
-/**
- *
- *
- *
- */
-public abstract class GenericPropertyFunction extends PFuncSimple {
-
- private final GenericFilterFunction filterFunction;
-
- public GenericPropertyFunction(GenericFilterFunction filterFunction) {
- this.filterFunction = filterFunction;
- }
-
- @Override
- public QueryIterator execEvaluated(Binding binding, Node subject, Node
predicate, Node object, ExecutionContext execCxt) {
- if (object.isLiteral()) {
- //These Property Functions do not accept literals as objects so
exit quickly.
- return QueryIterNullIterator.create(execCxt);
- }
-
- if (subject.isConcrete() && object.isConcrete()) {
- //Both are bound.
- return bothBound(binding, subject, predicate, object, execCxt);
- } else if (subject.isVariable() && object.isVariable()) {
- //Both are unbound.
- return bothUnbound(binding, subject, predicate, object, execCxt);
- } else {
- //One bound and one unbound.
- return oneBound(binding, subject, predicate, object, execCxt);
- }
-
- }
-
- private QueryIterator bothBound(Binding binding, Node subject, Node
predicate, Node object, ExecutionContext execCxt) {
-
- Graph graph = execCxt.getActiveGraph();
- QueryRewriteIndex queryRewriteIndex =
QueryRewriteIndex.retrieve(execCxt);
-
- Boolean isPositiveResult = queryRewrite(graph, subject, predicate,
object, queryRewriteIndex);
- if (isPositiveResult) {
- //Filter function test succeded so retain binding.
- return QueryIterSingleton.create(binding, execCxt);
- } else {
- //Filter function test failed so null result.
- return QueryIterNullIterator.create(execCxt);
- }
-
- }
-
- private QueryIterator bothUnbound(Binding binding, Node subject, Node
predicate, Node object, ExecutionContext execCxt) {
-
- QueryIterConcat queryIterConcat = new QueryIterConcat(execCxt);
- Var subjectVar = Var.alloc(subject.getName());
-
- Graph graph = execCxt.getActiveGraph();
-
- //Search for both Features and Geometry in the Graph. Reliant upon
consistent usage of SpatialObject (which is base class of Feature and Geometry)
if present.
- ExtendedIterator<Triple> subjectTriples;
- if (graph.contains(null, RDF.type.asNode(), Geo.SPATIAL_OBJECT_NODE)) {
- subjectTriples = graph.find(null, RDF.type.asNode(),
Geo.SPATIAL_OBJECT_NODE);
- } else if (graph.contains(null, RDF.type.asNode(), Geo.FEATURE_NODE)
|| graph.contains(null, RDF.type.asNode(), Geo.GEOMETRY_NODE)) {
- ExtendedIterator<Triple> featureTriples = graph.find(null,
RDF.type.asNode(), Geo.FEATURE_NODE);
- ExtendedIterator<Triple> geometryTriples = graph.find(null,
RDF.type.asNode(), Geo.GEOMETRY_NODE);
- subjectTriples = featureTriples.andThen(geometryTriples);
- } else {
- //Check for Geo Predicate Features in the Graph if no
GeometryLiterals found.
- subjectTriples = graph.find(null, SpatialExtension.GEO_LAT_NODE,
null);
- }
-
- //Bind all the Spatial Objects or Geo Predicates once as the subject
and search for corresponding Objects.
- while (subjectTriples.hasNext()) {
- Triple subjectTriple = subjectTriples.next();
- Node boundSubject = subjectTriple.getSubject();
- Binding subjectBind = BindingFactory.binding(binding, subjectVar,
boundSubject);
- QueryIterator queryIter = oneBound(subjectBind, boundSubject,
predicate, object, execCxt);
- queryIterConcat.add(queryIter);
- }
-
- return queryIterConcat;
- }
-
- private QueryIterator oneBound(Binding binding, Node subject, Node
predicate, Node object, ExecutionContext execCxt) {
-
- Graph graph = execCxt.getActiveGraph();
- Node boundNode;
- Node unboundNode;
- Boolean isSubjectBound;
- if (subject.isConcrete()) {
- //Subject is bound, object is unbound.
- boundNode = subject;
- unboundNode = object;
- isSubjectBound = true;
- } else {
- //Object is bound, subject is unbound.
- boundNode = object;
- unboundNode = subject;
- isSubjectBound = false;
- }
-
- if (!(graph.contains(boundNode, RDF.type.asNode(),
Geo.SPATIAL_OBJECT_NODE) || graph.contains(boundNode, RDF.type.asNode(),
Geo.FEATURE_NODE) || graph.contains(boundNode, RDF.type.asNode(),
Geo.GEOMETRY_NODE))) {
- if (!graph.contains(boundNode, SpatialExtension.GEO_LAT_NODE,
null)) {
- //Bound node is not a Feature or a Geometry or has Geo
predicates so exit.
- return QueryIterNullIterator.create(execCxt);
- }
- }
-
- boolean isSpatialIndex = SpatialIndex.isDefined(execCxt);
- QueryIterConcat queryIterConcat;
- if (!isSpatialIndex || filterFunction.isDisjoint() ||
filterFunction.isDisconnected()) {
- //Disjointed so retrieve all cases.
- queryIterConcat = findAll(graph, boundNode, unboundNode, binding,
isSubjectBound, predicate, execCxt);
- } else {
- //Only retrieve those in the spatial index which are within same
bounding box.
- queryIterConcat = findIndex(graph, boundNode, unboundNode,
binding, isSubjectBound, predicate, execCxt);
- }
-
- return queryIterConcat;
- }
-
- private QueryIterConcat findAll(Graph graph, Node boundNode, Node
unboundNode, Binding binding, boolean isSubjectBound, Node predicate,
ExecutionContext execCxt) {
-
- //Prepare the results.
- Var unboundVar = Var.alloc(unboundNode.getName());
- QueryIterConcat queryIterConcat = new QueryIterConcat(execCxt);
-
- //Search for both Features and Geometry in the Graph. Reliant upon
consistent usage of SpatialObject (which is base class of Feature and Geometry)
if present.
- ExtendedIterator<Triple> spatialTriples;
- if (graph.contains(null, RDF.type.asNode(), Geo.SPATIAL_OBJECT_NODE)) {
- spatialTriples = graph.find(null, RDF.type.asNode(),
Geo.SPATIAL_OBJECT_NODE);
- } else if (graph.contains(null, RDF.type.asNode(), Geo.FEATURE_NODE)
|| graph.contains(null, RDF.type.asNode(), Geo.GEOMETRY_NODE)) {
- ExtendedIterator<Triple> featureTriples = graph.find(null,
RDF.type.asNode(), Geo.FEATURE_NODE);
- ExtendedIterator<Triple> geometryTriples = graph.find(null,
RDF.type.asNode(), Geo.GEOMETRY_NODE);
- spatialTriples = featureTriples.andThen(geometryTriples);
- } else {
- //Check for Geo Predicate Features in the Graph if no
GeometryLiterals found.
- spatialTriples = graph.find(null, SpatialExtension.GEO_LAT_NODE,
null);
- }
-
- while (spatialTriples.hasNext()) {
- Triple spatialTriple = spatialTriples.next();
- Node spatialNode = spatialTriple.getSubject();
- Binding newBind = BindingFactory.binding(binding, unboundVar,
spatialNode);
- QueryIterator queryIter;
- if (isSubjectBound) {
- queryIter = bothBound(newBind, boundNode, predicate,
spatialNode, execCxt);
- } else {
- queryIter = bothBound(newBind, spatialNode, predicate,
boundNode, execCxt);
- }
- queryIterConcat.add(queryIter);
- }
-
- return queryIterConcat;
- }
-
- private QueryIterConcat findIndex(Graph graph, Node boundNode, Node
unboundNode, Binding binding, boolean isSubjectBound, Node predicate,
ExecutionContext execCxt) throws ExprEvalException {
-
- try {
- //Prepare for results.
- Var unboundVar = Var.alloc(unboundNode.getName());
- QueryIterConcat queryIterConcat = new QueryIterConcat(execCxt);
-
- //Find the asserted triples.
- List<Node> assertedNodes = findAsserted(graph, boundNode,
isSubjectBound, predicate);
- for (Node node : assertedNodes) {
- Binding newBind = BindingFactory.binding(binding, unboundVar,
node);
- QueryIterator queryIter = QueryIterSingleton.create(newBind,
execCxt);
- queryIterConcat.add(queryIter);
- }
-
- //Find the GeometryLiteral of the Bound Node.
- SpatialObjectGeometryLiteral boundGeometryLiteral =
SpatialObjectGeometryLiteral.retrieve(graph, boundNode);
- if (!boundGeometryLiteral.isValid()) {
- //Bound Node is not a Feature or a Geometry or there is no
GeometryLiteral so exit.
- return queryIterConcat;
- }
-
- Node geometryLiteral = boundGeometryLiteral.getGeometryLiteral();
-
- //Perform the search of the Spatial Index of the Dataset.
- SpatialIndex spatialIndex = SpatialIndex.retrieve(execCxt);
- GeometryWrapper geom = GeometryWrapper.extract(geometryLiteral);
- GeometryWrapper transformedGeom =
geom.transform(spatialIndex.getSrsInfo());
- Envelope searchEnvelope = transformedGeom.getEnvelope();
- HashSet<Resource> features = spatialIndex.query(searchEnvelope);
-
- //Check each of the Features that match the search.
- for (Resource feature : features) {
- Node featureNode = feature.asNode();
-
- //Ensure not already an asserted node.
- if (!assertedNodes.contains(featureNode)) {
-
- Binding newBind = BindingFactory.binding(binding,
unboundVar, featureNode);
- QueryIterator queryIter;
- if (isSubjectBound) {
- queryIter = bothBound(newBind, boundNode, predicate,
featureNode, execCxt);
- } else {
- queryIter = bothBound(newBind, featureNode, predicate,
boundNode, execCxt);
- }
- queryIterConcat.add(queryIter);
- }
-
- //Also test all Geometry of the Features. All, some or one
Geometry may have matched.
- ExtendedIterator<Triple> featureGeometryTriples =
graph.find(feature.asNode(), Geo.HAS_GEOMETRY_NODE, null);
- while (featureGeometryTriples.hasNext()) {
- Triple unboundTriple = featureGeometryTriples.next();
- Node geomNode = unboundTriple.getObject();
-
- //Ensure not already an asserted node.
- if (!assertedNodes.contains(geomNode)) {
- Binding newBind = BindingFactory.binding(binding,
unboundVar, geomNode);
- QueryIterator queryIter;
- if (isSubjectBound) {
- queryIter = bothBound(newBind, boundNode,
predicate, geomNode, execCxt);
- } else {
- queryIter = bothBound(newBind, geomNode,
predicate, boundNode, execCxt);
- }
- queryIterConcat.add(queryIter);
- }
- }
- }
-
- return queryIterConcat;
- } catch (MismatchedDimensionException | TransformException |
FactoryException | SpatialIndexException ex) {
- throw new ExprEvalException(ex.getMessage() + ": " +
FmtUtils.stringForNode(boundNode) + ", " + FmtUtils.stringForNode(unboundNode)
+ ", " + FmtUtils.stringForNode(predicate), ex);
- }
- }
-
- private List<Node> findAsserted(Graph graph, Node boundNode, boolean
isSubjectBound, Node predicate) {
- List<Node> assertedNodes = new ArrayList<>();
- if (isSubjectBound) {
- ExtendedIterator<Triple> assertedTriples = graph.find(boundNode,
predicate, null);
- while (assertedTriples.hasNext()) {
- Node assertedNode = assertedTriples.next().getObject();
- assertedNodes.add(assertedNode);
- }
- } else {
- ExtendedIterator<Triple> assertedTriples = graph.find(null,
predicate, boundNode);
- while (assertedTriples.hasNext()) {
- Node assertedNode = assertedTriples.next().getSubject();
- assertedNodes.add(assertedNode);
- }
- }
- return assertedNodes;
- }
-
- protected final Boolean queryRewrite(Graph graph, Node subject, Node
predicate, Node object, QueryRewriteIndex queryRewriteIndex) {
-
- if (graph.contains(subject, predicate, object)) {
- //The graph contains the asserted triple, return the current
binding.
- return true;
- }
-
- //If query re-writing is disabled then exit - already checked that
graph does not contain the asserted relation.
- if (!queryRewriteIndex.isIndexActive()) {
- return false;
- }
-
- //Begin Query Re-write by finding the literals of the Feature or
Geometry.
- SpatialObjectGeometryLiteral subjectSpatialLiteral =
SpatialObjectGeometryLiteral.retrieve(graph, subject);
- if (!subjectSpatialLiteral.isValid()) {
- //Subject is not a Feature or a Geometry or there is no
GeometryLiteral so exit.
- return false;
- }
-
- SpatialObjectGeometryLiteral objectSpatialLiteral =
SpatialObjectGeometryLiteral.retrieve(graph, object);
- if (!objectSpatialLiteral.isValid()) {
- //Object is not a Feature or a Geometry or there is no
GeometryLiteral so exit.
- return false;
- }
-
- //Check the QueryRewriteIndex for the result.
- Boolean isPositive =
queryRewriteIndex.test(subjectSpatialLiteral.getGeometryLiteral(), predicate,
objectSpatialLiteral.getGeometryLiteral(), this);
- return isPositive;
- }
-
- public Boolean testFilterFunction(Node subjectGeometryLiteral, Node
objectGeometryLiteral) {
- return filterFunction.exec(subjectGeometryLiteral,
objectGeometryLiteral);
- }
-
-}
+/*
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jena.geosparql.geo.topological;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import org.apache.jena.geosparql.geof.topological.GenericFilterFunction;
+import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.index.QueryRewriteIndex;
+import org.apache.jena.geosparql.implementation.vocabulary.Geo;
+import org.apache.jena.geosparql.implementation.vocabulary.SpatialExtension;
+import org.apache.jena.geosparql.spatial.SpatialIndex;
+import org.apache.jena.geosparql.spatial.SpatialIndexException;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.sparql.core.Var;
+import org.apache.jena.sparql.engine.ExecutionContext;
+import org.apache.jena.sparql.engine.QueryIterator;
+import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.engine.binding.BindingFactory;
+import org.apache.jena.sparql.engine.iterator.QueryIterConcat;
+import org.apache.jena.sparql.engine.iterator.QueryIterNullIterator;
+import org.apache.jena.sparql.engine.iterator.QueryIterSingleton;
+import org.apache.jena.sparql.expr.ExprEvalException;
+import org.apache.jena.sparql.pfunction.PFuncSimple;
+import org.apache.jena.sparql.util.FmtUtils;
+import org.apache.jena.util.iterator.ExtendedIterator;
+import org.apache.jena.vocabulary.RDF;
+import org.locationtech.jts.geom.Envelope;
+import org.opengis.geometry.MismatchedDimensionException;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+
+/**
+ *
+ *
+ *
+ */
+public abstract class GenericPropertyFunction extends PFuncSimple {
+
+ private final GenericFilterFunction filterFunction;
+
+ public GenericPropertyFunction(GenericFilterFunction filterFunction) {
+ this.filterFunction = filterFunction;
+ }
+
+ @Override
+ public QueryIterator execEvaluated(Binding binding, Node subject, Node
predicate, Node object, ExecutionContext execCxt) {
+ // optionally accept bound literals for simpler usage
+
+// if (object.isLiteral()) {
+// //These Property Functions do not accept literals as objects so
exit quickly.
+// return QueryIterNullIterator.create(execCxt);
+// }
+
+ if (subject.isConcrete() && object.isConcrete()) {
+ //Both are bound.
+ return bothBound(binding, subject, predicate, object, execCxt);
+ } else if (subject.isVariable() && object.isVariable()) {
+ //Both are unbound.
+ return bothUnbound(binding, subject, predicate, object, execCxt);
+ } else {
+ //One bound and one unbound.
+ return oneBound(binding, subject, predicate, object, execCxt);
+ }
+
+ }
+
+ private QueryIterator bothBound(Binding binding, Node subject, Node
predicate, Node object, ExecutionContext execCxt) {
+
+ Graph graph = execCxt.getActiveGraph();
+ QueryRewriteIndex queryRewriteIndex =
QueryRewriteIndex.retrieve(execCxt);
+
+ Boolean isPositiveResult = queryRewrite(graph, subject, predicate,
object, queryRewriteIndex);
+ if (isPositiveResult) {
+ //Filter function test succeded so retain binding.
+ return QueryIterSingleton.create(binding, execCxt);
+ } else {
+ //Filter function test failed so null result.
+ return QueryIterNullIterator.create(execCxt);
+ }
+
+ }
+
+ private QueryIterator bothUnbound(Binding binding, Node subject, Node
predicate, Node object, ExecutionContext execCxt) {
+
+ QueryIterConcat queryIterConcat = new QueryIterConcat(execCxt);
+ Var subjectVar = Var.alloc(subject.getName());
+
+ Graph graph = execCxt.getActiveGraph();
+
+ //Search for both Features and Geometry in the Graph. Reliant upon
consistent usage of SpatialObject (which is base class of Feature and Geometry)
if present.
+ ExtendedIterator<Triple> subjectTriples;
+ if (graph.contains(null, RDF.type.asNode(), Geo.SPATIAL_OBJECT_NODE)) {
+ subjectTriples = graph.find(null, RDF.type.asNode(),
Geo.SPATIAL_OBJECT_NODE);
+ } else if (graph.contains(null, RDF.type.asNode(), Geo.FEATURE_NODE)
|| graph.contains(null, RDF.type.asNode(), Geo.GEOMETRY_NODE)) {
+ ExtendedIterator<Triple> featureTriples = graph.find(null,
RDF.type.asNode(), Geo.FEATURE_NODE);
+ ExtendedIterator<Triple> geometryTriples = graph.find(null,
RDF.type.asNode(), Geo.GEOMETRY_NODE);
+ subjectTriples = featureTriples.andThen(geometryTriples);
+ } else {
+ //Check for Geo Predicate Features in the Graph if no
GeometryLiterals found.
+ subjectTriples = graph.find(null, SpatialExtension.GEO_LAT_NODE,
null);
+ }
+
+ //Bind all the Spatial Objects or Geo Predicates once as the subject
and search for corresponding Objects.
+ while (subjectTriples.hasNext()) {
+ Triple subjectTriple = subjectTriples.next();
+ Node boundSubject = subjectTriple.getSubject();
+ Binding subjectBind = BindingFactory.binding(binding, subjectVar,
boundSubject);
+ QueryIterator queryIter = oneBound(subjectBind, boundSubject,
predicate, object, execCxt);
+ queryIterConcat.add(queryIter);
+ }
+
+ return queryIterConcat;
+ }
+
+ private QueryIterator oneBound(Binding binding, Node subject, Node
predicate, Node object, ExecutionContext execCxt) {
+
+ Graph graph = execCxt.getActiveGraph();
+ Node boundNode;
+ Node unboundNode;
+ Boolean isSubjectBound;
+ if (subject.isConcrete()) {
+ //Subject is bound, object is unbound.
+ boundNode = subject;
+ unboundNode = object;
+ isSubjectBound = true;
+ } else {
+ //Object is bound, subject is unbound.
+ boundNode = object;
+ unboundNode = subject;
+ isSubjectBound = false;
+ }
+
+ if (!(boundNode.isLiteral() || graph.contains(boundNode,
RDF.type.asNode(), Geo.SPATIAL_OBJECT_NODE) || graph.contains(boundNode,
RDF.type.asNode(), Geo.FEATURE_NODE) || graph.contains(boundNode,
RDF.type.asNode(), Geo.GEOMETRY_NODE))) {
+ if (!graph.contains(boundNode, SpatialExtension.GEO_LAT_NODE,
null)) {
+ //Bound node is not a Feature or a Geometry or has Geo
predicates so exit.
+ return QueryIterNullIterator.create(execCxt);
+ }
+ }
+
+ boolean isSpatialIndex = SpatialIndex.isDefined(execCxt);
+ QueryIterConcat queryIterConcat;
+ if (!isSpatialIndex || filterFunction.isDisjoint() ||
filterFunction.isDisconnected()) {
+ //Disjointed so retrieve all cases.
+ queryIterConcat = findAll(graph, boundNode, unboundNode, binding,
isSubjectBound, predicate, execCxt);
+ } else {
+ //Only retrieve those in the spatial index which are within same
bounding box.
+ queryIterConcat = findIndex(graph, boundNode, unboundNode,
binding, isSubjectBound, predicate, execCxt);
+ }
+
+ return queryIterConcat;
+ }
+
+ private QueryIterConcat findAll(Graph graph, Node boundNode, Node
unboundNode, Binding binding, boolean isSubjectBound, Node predicate,
ExecutionContext execCxt) {
+
+ //Prepare the results.
+ Var unboundVar = Var.alloc(unboundNode.getName());
+ QueryIterConcat queryIterConcat = new QueryIterConcat(execCxt);
+
+ //Search for both Features and Geometry in the Graph. Reliant upon
consistent usage of SpatialObject (which is base class of Feature and Geometry)
if present.
+ ExtendedIterator<Triple> spatialTriples;
+ if (graph.contains(null, RDF.type.asNode(), Geo.SPATIAL_OBJECT_NODE)) {
+ spatialTriples = graph.find(null, RDF.type.asNode(),
Geo.SPATIAL_OBJECT_NODE);
+ } else if (graph.contains(null, RDF.type.asNode(), Geo.FEATURE_NODE)
|| graph.contains(null, RDF.type.asNode(), Geo.GEOMETRY_NODE)) {
+ ExtendedIterator<Triple> featureTriples = graph.find(null,
RDF.type.asNode(), Geo.FEATURE_NODE);
+ ExtendedIterator<Triple> geometryTriples = graph.find(null,
RDF.type.asNode(), Geo.GEOMETRY_NODE);
+ spatialTriples = featureTriples.andThen(geometryTriples);
+ } else {
+ //Check for Geo Predicate Features in the Graph if no
GeometryLiterals found.
+ spatialTriples = graph.find(null, SpatialExtension.GEO_LAT_NODE,
null);
+ }
+
+ while (spatialTriples.hasNext()) {
+ Triple spatialTriple = spatialTriples.next();
+ Node spatialNode = spatialTriple.getSubject();
+ Binding newBind = BindingFactory.binding(binding, unboundVar,
spatialNode);
+ QueryIterator queryIter;
+ if (isSubjectBound) {
+ queryIter = bothBound(newBind, boundNode, predicate,
spatialNode, execCxt);
+ } else {
+ queryIter = bothBound(newBind, spatialNode, predicate,
boundNode, execCxt);
+ }
+ queryIterConcat.add(queryIter);
+ }
+
+ return queryIterConcat;
+ }
+
+ private QueryIterConcat findIndex(Graph graph, Node boundNode, Node
unboundNode, Binding binding, boolean isSubjectBound, Node predicate,
ExecutionContext execCxt) throws ExprEvalException {
+
+ try {
+ //Prepare for results.
+ Var unboundVar = Var.alloc(unboundNode.getName());
+ QueryIterConcat queryIterConcat = new QueryIterConcat(execCxt);
+
+ //Find the asserted triples.
+ List<Node> assertedNodes = !isSubjectBound ||
!boundNode.isLiteral() ? findAsserted(graph, boundNode, isSubjectBound,
predicate) : Collections.emptyList();
+ for (Node node : assertedNodes) {
+ Binding newBind = BindingFactory.binding(binding, unboundVar,
node);
+ QueryIterator queryIter = QueryIterSingleton.create(newBind,
execCxt);
+ queryIterConcat.add(queryIter);
+ }
+
+ //Find the GeometryLiteral of the Bound Node.
+ SpatialObjectGeometryLiteral boundGeometryLiteral =
SpatialObjectGeometryLiteral.retrieve(graph, boundNode);
+ if (!boundGeometryLiteral.isValid()) {
+ //Bound Node is not a Feature or a Geometry or there is no
GeometryLiteral so exit.
+ return queryIterConcat;
+ }
+
+ Node geometryLiteral = boundGeometryLiteral.getGeometryLiteral();
+
+ //Perform the search of the Spatial Index of the Dataset.
+ SpatialIndex spatialIndex = SpatialIndex.retrieve(execCxt);
+ GeometryWrapper geom = GeometryWrapper.extract(geometryLiteral);
+ GeometryWrapper transformedGeom =
geom.transform(spatialIndex.getSrsInfo());
+ Envelope searchEnvelope = transformedGeom.getEnvelope();
+ HashSet<Resource> features = spatialIndex.query(searchEnvelope);
+
+ //Check each of the Features that match the search.
+ for (Resource feature : features) {
+ Node featureNode = feature.asNode();
+
+ //Ensure not already an asserted node.
+ if (!assertedNodes.contains(featureNode)) {
+
+ Binding newBind = BindingFactory.binding(binding,
unboundVar, featureNode);
+ QueryIterator queryIter;
+ if (isSubjectBound) {
+ queryIter = bothBound(newBind, boundNode, predicate,
featureNode, execCxt);
+ } else {
+ queryIter = bothBound(newBind, featureNode, predicate,
boundNode, execCxt);
+ }
+ queryIterConcat.add(queryIter);
+ }
+
+ //Also test all Geometry of the Features. All, some or one
Geometry may have matched.
+ ExtendedIterator<Triple> featureGeometryTriples =
graph.find(feature.asNode(), Geo.HAS_GEOMETRY_NODE, null);
+ while (featureGeometryTriples.hasNext()) {
+ Triple unboundTriple = featureGeometryTriples.next();
+ Node geomNode = unboundTriple.getObject();
+
+ //Ensure not already an asserted node.
+ if (!assertedNodes.contains(geomNode)) {
+ Binding newBind = BindingFactory.binding(binding,
unboundVar, geomNode);
+ QueryIterator queryIter;
+ if (isSubjectBound) {
+ queryIter = bothBound(newBind, boundNode,
predicate, geomNode, execCxt);
+ } else {
+ queryIter = bothBound(newBind, geomNode,
predicate, boundNode, execCxt);
+ }
+ queryIterConcat.add(queryIter);
+ }
+ }
+ }
+
+ return queryIterConcat;
+ } catch (MismatchedDimensionException | TransformException |
FactoryException | SpatialIndexException ex) {
+ throw new ExprEvalException(ex.getMessage() + ": " +
FmtUtils.stringForNode(boundNode) + ", " + FmtUtils.stringForNode(unboundNode)
+ ", " + FmtUtils.stringForNode(predicate), ex);
+ }
+ }
+
+ private List<Node> findAsserted(Graph graph, Node boundNode, boolean
isSubjectBound, Node predicate) {
+ List<Node> assertedNodes = new ArrayList<>();
+ if (isSubjectBound) {
+ ExtendedIterator<Triple> assertedTriples = graph.find(boundNode,
predicate, null);
+ while (assertedTriples.hasNext()) {
+ Node assertedNode = assertedTriples.next().getObject();
+ assertedNodes.add(assertedNode);
+ }
+ } else {
+ ExtendedIterator<Triple> assertedTriples = graph.find(null,
predicate, boundNode);
+ while (assertedTriples.hasNext()) {
+ Node assertedNode = assertedTriples.next().getSubject();
+ assertedNodes.add(assertedNode);
+ }
+ }
+ return assertedNodes;
+ }
+
+ protected final Boolean queryRewrite(Graph graph, Node subject, Node
predicate, Node object, QueryRewriteIndex queryRewriteIndex) {
+
+ if (graph.contains(subject, predicate, object)) {
+ //The graph contains the asserted triple, return the current
binding.
+ return true;
+ }
+
+ //If query re-writing is disabled then exit - already checked that
graph does not contain the asserted relation.
+ if (!queryRewriteIndex.isIndexActive()) {
+ return false;
+ }
+
+ //Begin Query Re-write by finding the literals of the Feature or
Geometry.
+ SpatialObjectGeometryLiteral subjectSpatialLiteral =
SpatialObjectGeometryLiteral.retrieve(graph, subject);
+ if (!subjectSpatialLiteral.isValid()) {
+ //Subject is not a Feature or a Geometry or there is no
GeometryLiteral so exit.
+ return false;
+ }
+
+ SpatialObjectGeometryLiteral objectSpatialLiteral =
SpatialObjectGeometryLiteral.retrieve(graph, object);
+ if (!objectSpatialLiteral.isValid()) {
+ //Object is not a Feature or a Geometry or there is no
GeometryLiteral so exit.
+ return false;
+ }
+
+ //Check the QueryRewriteIndex for the result.
+ Boolean isPositive =
queryRewriteIndex.test(subjectSpatialLiteral.getGeometryLiteral(), predicate,
objectSpatialLiteral.getGeometryLiteral(), this);
+ return isPositive;
+ }
+
+ public Boolean testFilterFunction(Node subjectGeometryLiteral, Node
objectGeometryLiteral) {
+ return filterFunction.exec(subjectGeometryLiteral,
objectGeometryLiteral);
+ }
+
+}
diff --git
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/SpatialObjectGeometryLiteral.java
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/SpatialObjectGeometryLiteral.java
index 510b87db14..452cd7fba3 100644
---
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/SpatialObjectGeometryLiteral.java
+++
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/SpatialObjectGeometryLiteral.java
@@ -19,11 +19,13 @@ package org.apache.jena.geosparql.geo.topological;
import java.util.Objects;
import org.apache.jena.datatypes.DatatypeFormatException;
+import org.apache.jena.geosparql.implementation.datatype.GeometryDatatype;
import org.apache.jena.geosparql.implementation.vocabulary.Geo;
import org.apache.jena.geosparql.implementation.vocabulary.SpatialExtension;
import org.apache.jena.geosparql.spatial.ConvertLatLon;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
import org.apache.jena.graph.Triple;
import org.apache.jena.util.iterator.ExtendedIterator;
import org.apache.jena.vocabulary.RDF;
@@ -102,6 +104,14 @@ public class SpatialObjectGeometryLiteral {
protected static final SpatialObjectGeometryLiteral retrieve(Graph graph,
Node targetSpatialObject) {
Node geometry = null;
+ if (targetSpatialObject != null && targetSpatialObject.isLiteral()) {
+ if (targetSpatialObject.getLiteralDatatype() instanceof
GeometryDatatype) {
+ return new
SpatialObjectGeometryLiteral(NodeFactory.createBlankNode(),
targetSpatialObject);
+ } else {
+ throw new
DatatypeFormatException(targetSpatialObject.getLiteralLexicalForm() + " is no
Geometry literal");
+ }
+ }
+
if (graph.contains(targetSpatialObject, RDF.type.asNode(),
Geo.FEATURE_NODE)) {
//Target is Feature - find the default Geometry.
ExtendedIterator<Triple> geomIter =
graph.find(targetSpatialObject, Geo.HAS_DEFAULT_GEOMETRY_NODE, null);
diff --git
a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geo/topological/GenericPropertyFunctionTest.java
b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geo/topological/GenericPropertyFunctionTest.java
index 6589565ce0..2d3df370e9 100644
---
a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geo/topological/GenericPropertyFunctionTest.java
+++
b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geo/topological/GenericPropertyFunctionTest.java
@@ -583,4 +583,37 @@ public class GenericPropertyFunctionTest {
assertEquals(expResult, result);
}
+ /**
+ * Test of execEvaluated method, of class GenericPropertyFunction.
+ * Geo extension: test lookup using geo-literal
+ */
+ @Test
+ public void testExecEvaluated_object_is_literal() {
+
+
+ String query = "PREFIX geo: <http://www.opengis.net/ont/geosparql#>\n"
+ + "\n"
+ + "SELECT ?subj\n"
+ + "WHERE{\n"
+ + " BIND(\"<http://www.opengis.net/def/crs/EPSG/0/27700>
POINT(6 7)\"^^geo:wktLiteral AS ?lit) \n"
+ + " ?subj geo:sfContains ?lit .\n"
+ + "}ORDER by ?subj";
+
+ List<Resource> results = new ArrayList<>();
+ try (QueryExecution qe = QueryExecutionFactory.create(query, dataset))
{
+ ResultSet rs = qe.execSelect();
+ while (rs.hasNext()) {
+ QuerySolution qs = rs.nextSolution();
+
+ Resource result = qs.getResource("subj");
+ results.add(result);
+ }
+ }
+
+ List<Resource> expResults = Arrays.asList(FEATURE_A, GEOMETRY_A);
+
+ //
+ //
+ assertEquals(expResults, results);
+ }
}