17
17
*/
18
18
package org .apache .jena .geosparql .geo .topological ;
19
19
20
- import java .util .ArrayList ;
21
- import java .util .Collections ;
20
+ import java .util .Collection ;
22
21
import java .util .HashSet ;
23
22
import java .util .List ;
23
+
24
+ import org .apache .jena .atlas .iterator .Iter ;
24
25
import org .apache .jena .geosparql .geof .topological .GenericFilterFunction ;
25
26
import org .apache .jena .geosparql .implementation .GeometryWrapper ;
26
27
import org .apache .jena .geosparql .implementation .index .QueryRewriteIndex ;
37
38
import org .apache .jena .sparql .engine .QueryIterator ;
38
39
import org .apache .jena .sparql .engine .binding .Binding ;
39
40
import org .apache .jena .sparql .engine .binding .BindingFactory ;
41
+ import org .apache .jena .sparql .engine .iterator .QueryIter ;
40
42
import org .apache .jena .sparql .engine .iterator .QueryIterConcat ;
41
43
import org .apache .jena .sparql .engine .iterator .QueryIterNullIterator ;
44
+ import org .apache .jena .sparql .engine .iterator .QueryIterPlainWrapper ;
42
45
import org .apache .jena .sparql .engine .iterator .QueryIterSingleton ;
43
46
import org .apache .jena .sparql .expr .ExprEvalException ;
44
47
import org .apache .jena .sparql .pfunction .PFuncSimple ;
@@ -83,14 +86,18 @@ public QueryIterator execEvaluated(Binding binding, Node subject, Node predicate
83
86
//One bound and one unbound.
84
87
return oneBound (binding , subject , predicate , object , execCxt );
85
88
}
89
+ }
86
90
91
+ private QueryIterator bothBound (Binding binding , boolean isSubjectBound , Node subject , Node predicate , Node object , ExecutionContext execCxt ) {
92
+ QueryIterator iter = isSubjectBound
93
+ ? bothBound (binding , subject , predicate , object , execCxt )
94
+ : bothBound (binding , object , predicate , subject , execCxt );
95
+ return iter ;
87
96
}
88
97
89
98
private QueryIterator bothBound (Binding binding , Node subject , Node predicate , Node object , ExecutionContext execCxt ) {
90
-
91
99
Graph graph = execCxt .getActiveGraph ();
92
100
QueryRewriteIndex queryRewriteIndex = QueryRewriteIndex .retrieve (execCxt );
93
-
94
101
Boolean isPositiveResult = queryRewrite (graph , subject , predicate , object , queryRewriteIndex );
95
102
if (isPositiveResult ) {
96
103
//Filter function test succeded so retain binding.
@@ -99,47 +106,33 @@ private QueryIterator bothBound(Binding binding, Node subject, Node predicate, N
99
106
//Filter function test failed so null result.
100
107
return QueryIterNullIterator .create (execCxt );
101
108
}
102
-
103
109
}
104
110
105
111
private QueryIterator bothUnbound (Binding binding , Node subject , Node predicate , Node object , ExecutionContext execCxt ) {
106
-
107
- QueryIterConcat queryIterConcat = new QueryIterConcat (execCxt );
108
112
Var subjectVar = Var .alloc (subject .getName ());
109
113
110
114
Graph graph = execCxt .getActiveGraph ();
111
115
112
116
//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.
113
- ExtendedIterator <Triple > subjectTriples ;
114
- if (graph .contains (null , RDF .type .asNode (), Geo .SPATIAL_OBJECT_NODE )) {
115
- subjectTriples = graph .find (null , RDF .type .asNode (), Geo .SPATIAL_OBJECT_NODE );
116
- } else if (graph .contains (null , RDF .type .asNode (), Geo .FEATURE_NODE ) || graph .contains (null , RDF .type .asNode (), Geo .GEOMETRY_NODE )) {
117
- ExtendedIterator <Triple > featureTriples = graph .find (null , RDF .type .asNode (), Geo .FEATURE_NODE );
118
- ExtendedIterator <Triple > geometryTriples = graph .find (null , RDF .type .asNode (), Geo .GEOMETRY_NODE );
119
- subjectTriples = featureTriples .andThen (geometryTriples );
120
- } else {
121
- //Check for Geo Predicate Features in the Graph if no GeometryLiterals found.
122
- subjectTriples = graph .find (null , SpatialExtension .GEO_LAT_NODE , null );
123
- }
124
-
125
- //Bind all the Spatial Objects or Geo Predicates once as the subject and search for corresponding Objects.
126
- while (subjectTriples .hasNext ()) {
127
- Triple subjectTriple = subjectTriples .next ();
128
- Node boundSubject = subjectTriple .getSubject ();
129
- Binding subjectBind = BindingFactory .binding (binding , subjectVar , boundSubject );
130
- QueryIterator queryIter = oneBound (subjectBind , boundSubject , predicate , object , execCxt );
131
- queryIterConcat .add (queryIter );
132
- }
133
-
134
- return queryIterConcat ;
117
+ ExtendedIterator <Triple > spatialTriples = findSpatialTriples (graph );
118
+ ExtendedIterator <Binding > iterator = spatialTriples
119
+ .mapWith (Triple ::getSubject )
120
+ .mapWith (node -> BindingFactory .binding (binding , subjectVar , node ));
121
+
122
+ QueryIter queryIter = QueryIter .flatMap (
123
+ QueryIterPlainWrapper .create (iterator , execCxt ),
124
+ b -> oneBound (b , b .get (subjectVar ), predicate , object , execCxt ),
125
+ execCxt
126
+ );
127
+ return queryIter ;
135
128
}
136
129
137
130
private QueryIterator oneBound (Binding binding , Node subject , Node predicate , Node object , ExecutionContext execCxt ) {
138
131
139
132
Graph graph = execCxt .getActiveGraph ();
140
133
Node boundNode ;
141
134
Node unboundNode ;
142
- Boolean isSubjectBound ;
135
+ boolean isSubjectBound ;
143
136
if (subject .isConcrete ()) {
144
137
//Subject is bound, object is unbound.
145
138
boundNode = subject ;
@@ -152,33 +145,51 @@ private QueryIterator oneBound(Binding binding, Node subject, Node predicate, No
152
145
isSubjectBound = false ;
153
146
}
154
147
155
- 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 ))) {
148
+ if (!(boundNode .isLiteral () ||
149
+ graph .contains (boundNode , RDF .type .asNode (), Geo .SPATIAL_OBJECT_NODE ) ||
150
+ graph .contains (boundNode , RDF .type .asNode (), Geo .FEATURE_NODE ) ||
151
+ graph .contains (boundNode , RDF .type .asNode (), Geo .GEOMETRY_NODE ))) {
156
152
if (!graph .contains (boundNode , SpatialExtension .GEO_LAT_NODE , null )) {
157
153
//Bound node is not a Feature or a Geometry or has Geo predicates so exit.
158
154
return QueryIterNullIterator .create (execCxt );
159
155
}
160
156
}
161
157
162
158
boolean isSpatialIndex = SpatialIndex .isDefined (execCxt );
163
- QueryIterConcat queryIterConcat ;
159
+ QueryIterator result ;
164
160
if (!isSpatialIndex || filterFunction .isDisjoint () || filterFunction .isDisconnected ()) {
165
161
//Disjointed so retrieve all cases.
166
- queryIterConcat = findAll (graph , boundNode , unboundNode , binding , isSubjectBound , predicate , execCxt );
162
+ result = findAll (graph , boundNode , unboundNode , binding , isSubjectBound , predicate , execCxt );
167
163
} else {
168
164
//Only retrieve those in the spatial index which are within same bounding box.
169
- queryIterConcat = findIndex (graph , boundNode , unboundNode , binding , isSubjectBound , predicate , execCxt );
165
+ result = findIndex (graph , boundNode , unboundNode , binding , isSubjectBound , predicate , execCxt );
170
166
}
171
-
172
- return queryIterConcat ;
167
+ return result ;
173
168
}
174
169
175
- private QueryIterConcat findAll (Graph graph , Node boundNode , Node unboundNode , Binding binding , boolean isSubjectBound , Node predicate , ExecutionContext execCxt ) {
170
+ private QueryIterator findAll (Graph graph , Node boundNode , Node unboundNode , Binding binding , boolean isSubjectBound , Node predicate , ExecutionContext execCxt ) {
176
171
177
172
//Prepare the results.
178
173
Var unboundVar = Var .alloc (unboundNode .getName ());
179
- QueryIterConcat queryIterConcat = new QueryIterConcat (execCxt );
180
174
181
175
//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.
176
+ ExtendedIterator <Triple > spatialTriples = findSpatialTriples (graph );
177
+
178
+ ExtendedIterator <Binding > iterator = spatialTriples
179
+ .mapWith (Triple ::getSubject )
180
+ .mapWith (node -> BindingFactory .binding (binding , unboundVar , node ));
181
+
182
+ return QueryIter .flatMap (
183
+ QueryIterPlainWrapper .create (iterator , execCxt ),
184
+ b -> {
185
+ Node spatialNode = b .get (unboundVar );
186
+ QueryIterator iter = bothBound (b , isSubjectBound , boundNode , predicate , spatialNode , execCxt );
187
+ return iter ;
188
+ },
189
+ execCxt );
190
+ }
191
+
192
+ private static ExtendedIterator <Triple > findSpatialTriples (Graph graph ) {
182
193
ExtendedIterator <Triple > spatialTriples ;
183
194
if (graph .contains (null , RDF .type .asNode (), Geo .SPATIAL_OBJECT_NODE )) {
184
195
spatialTriples = graph .find (null , RDF .type .asNode (), Geo .SPATIAL_OBJECT_NODE );
@@ -190,45 +201,33 @@ private QueryIterConcat findAll(Graph graph, Node boundNode, Node unboundNode, B
190
201
//Check for Geo Predicate Features in the Graph if no GeometryLiterals found.
191
202
spatialTriples = graph .find (null , SpatialExtension .GEO_LAT_NODE , null );
192
203
}
193
-
194
- while (spatialTriples .hasNext ()) {
195
- Triple spatialTriple = spatialTriples .next ();
196
- Node spatialNode = spatialTriple .getSubject ();
197
- Binding newBind = BindingFactory .binding (binding , unboundVar , spatialNode );
198
- QueryIterator queryIter ;
199
- if (isSubjectBound ) {
200
- queryIter = bothBound (newBind , boundNode , predicate , spatialNode , execCxt );
201
- } else {
202
- queryIter = bothBound (newBind , spatialNode , predicate , boundNode , execCxt );
203
- }
204
- queryIterConcat .add (queryIter );
205
- }
206
-
207
- return queryIterConcat ;
204
+ return spatialTriples ;
208
205
}
209
206
210
- private QueryIterConcat findIndex (Graph graph , Node boundNode , Node unboundNode , Binding binding , boolean isSubjectBound , Node predicate , ExecutionContext execCxt ) throws ExprEvalException {
211
-
207
+ private QueryIterator findIndex (Graph graph , Node boundNode , Node unboundNode , Binding binding , boolean isSubjectBound , Node predicate , ExecutionContext execCxt ) throws ExprEvalException {
212
208
try {
213
209
//Prepare for results.
214
- Var unboundVar = Var .alloc (unboundNode .getName ());
215
- QueryIterConcat queryIterConcat = new QueryIterConcat (execCxt );
210
+ Var unboundVar = Var .alloc (unboundNode );
216
211
217
212
//Find the asserted triples.
218
- List <Node > assertedNodes = !isSubjectBound || !boundNode .isLiteral () ? findAsserted (graph , boundNode , isSubjectBound , predicate ) : Collections .emptyList ();
219
- for (Node node : assertedNodes ) {
220
- Binding newBind = BindingFactory .binding (binding , unboundVar , node );
221
- QueryIterator queryIter = QueryIterSingleton .create (newBind , execCxt );
222
- queryIterConcat .add (queryIter );
223
- }
213
+ Collection <Node > assertedNodes = !isSubjectBound || !boundNode .isLiteral ()
214
+ ? findAsserted (graph , boundNode , isSubjectBound , predicate )
215
+ : List .of ();
216
+
217
+ QueryIterator assertedNodesIter = QueryIterPlainWrapper .create (
218
+ Iter .map (assertedNodes .iterator (), node -> BindingFactory .binding (binding , unboundVar , node )),
219
+ execCxt );
224
220
225
221
//Find the GeometryLiteral of the Bound Node.
226
222
SpatialObjectGeometryLiteral boundGeometryLiteral = SpatialObjectGeometryLiteral .retrieve (graph , boundNode );
227
223
if (!boundGeometryLiteral .isValid ()) {
228
224
//Bound Node is not a Feature or a Geometry or there is no GeometryLiteral so exit.
229
- return queryIterConcat ;
225
+ return assertedNodesIter ;
230
226
}
231
227
228
+ QueryIterConcat queryIterConcat = new QueryIterConcat (execCxt );
229
+ queryIterConcat .add (assertedNodesIter );
230
+
232
231
Node geometryLiteral = boundGeometryLiteral .getGeometryLiteral ();
233
232
234
233
//Perform the search of the Spatial Index of the Dataset.
@@ -238,55 +237,66 @@ private QueryIterConcat findIndex(Graph graph, Node boundNode, Node unboundNode,
238
237
Envelope searchEnvelope = transformedGeom .getEnvelope ();
239
238
HashSet <Resource > features = spatialIndex .query (searchEnvelope );
240
239
241
- //Check each of the Features that match the search.
242
- for (Resource feature : features ) {
243
- Node featureNode = feature .asNode ();
244
-
245
- //Ensure not already an asserted node.
246
- if (!assertedNodes .contains (featureNode )) {
247
-
248
- Binding newBind = BindingFactory .binding (binding , unboundVar , featureNode );
249
- QueryIterator queryIter ;
250
- if (isSubjectBound ) {
251
- queryIter = bothBound (newBind , boundNode , predicate , featureNode , execCxt );
252
- } else {
253
- queryIter = bothBound (newBind , featureNode , predicate , boundNode , execCxt );
254
- }
255
- queryIterConcat .add (queryIter );
256
- }
257
-
258
- //Also test all Geometry of the Features. All, some or one Geometry may have matched.
259
- List <Node > featureGeometryTriples = G .listSP (graph , feature .asNode (), Geo .HAS_GEOMETRY_NODE );
260
- for ( Node geomNode : featureGeometryTriples ) {
261
- //Ensure not already an asserted node.
262
- if (!assertedNodes .contains (geomNode )) {
263
- Binding newBind = BindingFactory .binding (binding , unboundVar , geomNode );
264
- QueryIterator queryIter ;
265
- if (isSubjectBound ) {
266
- queryIter = bothBound (newBind , boundNode , predicate , geomNode , execCxt );
267
- } else {
268
- queryIter = bothBound (newBind , geomNode , predicate , boundNode , execCxt );
269
- }
270
- queryIterConcat .add (queryIter );
271
- }
272
- }
273
- }
240
+ // Check each of the Features that match the search.
241
+ QueryIterator featuresIter = QueryIterPlainWrapper .create (
242
+ Iter .map (features .iterator (), feature -> BindingFactory .binding (binding , unboundVar , feature .asNode ())),
243
+ execCxt );
244
+
245
+ QueryIterator queryIterator = QueryIter .flatMap (featuresIter ,
246
+ featureBinding -> {
247
+ return findByFeature (graph , binding , featureBinding ,
248
+ isSubjectBound , boundNode , predicate , unboundVar ,
249
+ execCxt , assertedNodes );
250
+ },
251
+ execCxt );
252
+ queryIterConcat .add (queryIterator );
274
253
275
254
return queryIterConcat ;
276
255
} catch (MismatchedDimensionException | TransformException | FactoryException | SpatialIndexException ex ) {
277
256
throw new ExprEvalException (ex .getMessage () + ": " + FmtUtils .stringForNode (boundNode ) + ", " + FmtUtils .stringForNode (unboundNode ) + ", " + FmtUtils .stringForNode (predicate ), ex );
278
257
}
279
258
}
280
259
281
- private List <Node > findAsserted (Graph graph , Node boundNode , boolean isSubjectBound , Node predicate ) {
282
- List <Node > assertedNodes = new ArrayList <>();
283
- if (isSubjectBound ) {
284
- List <Node > x = G .listSP (graph , boundNode , predicate );
285
- assertedNodes .addAll (x );
286
- } else {
287
- List <Node > x = G .listPO (graph , predicate , boundNode );
288
- assertedNodes .addAll (x );
260
+ private QueryIterator findByFeature (Graph graph , Binding binding , Binding featureBinding ,
261
+ boolean isSubjectBound , Node boundNode , Node predicate , Var unboundVar ,
262
+ ExecutionContext execCxt , Collection <Node > assertedNodes ) {
263
+
264
+ Node featureNode = featureBinding .get (unboundVar );
265
+ QueryIterConcat featureIterConcat = new QueryIterConcat (execCxt );
266
+
267
+ // Check Features directly if not already asserted
268
+ if (!assertedNodes .contains (featureNode )) {
269
+ QueryIterator tmpIter = bothBound (featureBinding , isSubjectBound , boundNode , predicate , featureNode , execCxt );
270
+ featureIterConcat .add (tmpIter );
289
271
}
272
+
273
+ // Also test all Geometry of the Features. All, some or one Geometry may have matched.
274
+ ExtendedIterator <Node > featureGeometries = G .iterSP (graph , featureNode , Geo .HAS_GEOMETRY_NODE );
275
+ QueryIterator geometriesQueryIterator = QueryIterPlainWrapper .create (
276
+ Iter .map (
277
+ Iter .filter ( // omit asserted
278
+ featureGeometries ,
279
+ geometry -> !assertedNodes .contains (geometry )
280
+ ),
281
+ geometryNode -> BindingFactory .binding (binding , unboundVar , geometryNode )),
282
+ execCxt );
283
+
284
+ geometriesQueryIterator = QueryIter .flatMap (
285
+ geometriesQueryIterator ,
286
+ b2 -> {
287
+ Node geomNode = b2 .get (unboundVar );
288
+ return bothBound (b2 , isSubjectBound , boundNode , predicate , geomNode , execCxt );
289
+ },
290
+ execCxt );
291
+
292
+ featureIterConcat .add (geometriesQueryIterator );
293
+ return featureIterConcat ;
294
+ }
295
+
296
+ private List <Node > findAsserted (Graph graph , Node boundNode , boolean isSubjectBound , Node predicate ) {
297
+ List <Node > assertedNodes = isSubjectBound
298
+ ? G .listSP (graph , boundNode , predicate )
299
+ : G .listPO (graph , predicate , boundNode );
290
300
return assertedNodes ;
291
301
}
292
302
@@ -323,5 +333,4 @@ protected final Boolean queryRewrite(Graph graph, Node subject, Node predicate,
323
333
public Boolean testFilterFunction (Node subjectGeometryLiteral , Node objectGeometryLiteral ) {
324
334
return filterFunction .exec (subjectGeometryLiteral , objectGeometryLiteral );
325
335
}
326
-
327
336
}
0 commit comments