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 e4dcc30c7c GH-3473: GeoSparql: Remove need for 'a geo:Geometry' in 
GenericPropertyFunction; Spatial Indexer UI: Replace without selection clears 
index.
e4dcc30c7c is described below

commit e4dcc30c7ce0409e3cef0a267f21e6f7d8590074
Author: Claus Stadler <[email protected]>
AuthorDate: Tue Sep 30 18:12:50 2025 +0200

    GH-3473: GeoSparql: Remove need for 'a geo:Geometry' in 
GenericPropertyFunction; Spatial Indexer UI: Replace without selection clears 
index.
---
 .../org/apache/jena/rdfs/DatasetGraphRDFS.java     |   4 +
 .../apache/jena/sparql/core/DatasetGraphQuads.java |   2 +-
 .../jena/sparql/core/mem/DatasetGraphInMemory.java |   4 +-
 .../java/org/apache/jena/sparql/util/Context.java  |  40 +++-
 .../org/apache/jena/rdfs/TestDatasetGraphRDFS.java |   7 +
 .../geosparql/query/BenchmarkSpatialQueries.java   | 242 +++++++++++++++++++++
 .../jena/geosparql/query/SpatialQueryTask.java     |  27 +++
 .../jena/geosparql/query/SpatialQueryTask550.java  |  95 ++++++++
 .../geosparql/query/SpatialQueryTaskCurrent.java   |  84 +++++++
 .../mod/geosparql/SpatialIndexerService.java       |  22 +-
 .../src/main/resources/spatial-indexer/index.html  |   7 +-
 .../GenericGeometryPropertyFunction.java           |  60 ++---
 .../geo/topological/GenericPropertyFunction.java   | 152 ++++++-------
 .../topological/SpatialObjectGeometryLiteral.java  |  62 +++---
 .../implementation/access/AccessGeoSPARQL.java     | 232 ++++++++++++++++++++
 .../implementation/access/AccessWGS84.java         | 155 +++++++++++++
 .../implementation/index/QueryRewriteIndex.java    |  31 +--
 .../geosparql/spatial/SpatialIndexFindUtils.java   | 112 ++--------
 .../geosparql/spatial/index/v2/STRtreeUtils.java   |   2 +-
 .../spatial/index/v2/SpatialIndexLib.java          |   4 +-
 .../GenericSpatialPropertyFunction.java            |  59 ++---
 .../simple_features/SfPFMiscSparqlTest.java        |  99 +++++++++
 22 files changed, 1172 insertions(+), 330 deletions(-)

diff --git a/jena-arq/src/main/java/org/apache/jena/rdfs/DatasetGraphRDFS.java 
b/jena-arq/src/main/java/org/apache/jena/rdfs/DatasetGraphRDFS.java
index f5e96bf711..1ca880b9c8 100644
--- a/jena-arq/src/main/java/org/apache/jena/rdfs/DatasetGraphRDFS.java
+++ b/jena-arq/src/main/java/org/apache/jena/rdfs/DatasetGraphRDFS.java
@@ -67,6 +67,10 @@ public class DatasetGraphRDFS extends DatasetGraphWrapper 
implements DatasetGrap
         return new GraphRDFS(base, setup);
     }
 
+    @Override
+    public Iterator<Quad> find()
+    { return find(Node.ANY, Node.ANY, Node.ANY, Node.ANY); }
+
     // Quad-centric access
     @Override
     public Iterator<Quad> find(Quad quad) {
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphQuads.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphQuads.java
index 76da75d6bc..de7ee17100 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphQuads.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphQuads.java
@@ -44,7 +44,7 @@ public abstract class DatasetGraphQuads extends 
DatasetGraphBase
 
     @Override
     public void addGraph(Node graphName, Graph graph) {
-        graph.find().forEachRemaining(t -> add(Quad.create(graphName, t)));
+        graph.find().forEach(t -> add(Quad.create(graphName, t)));
     }
 
 //    @Override
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
index 07738018a1..7d0dc6935b 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
@@ -346,10 +346,10 @@ public class DatasetGraphInMemory extends 
DatasetGraphTriplesQuads implements Tr
     }
 
     private Consumer<Graph> addGraph(final Node name) {
-        return g -> g.find().mapWith(t -> new Quad(name, 
t)).forEachRemaining(this::add);
+        return g -> g.find().mapWith(t -> new Quad(name, 
t)).forEach(this::add);
     }
 
-    private final Consumer<Graph> removeGraph = g -> 
g.find().forEachRemaining(g::delete);
+    private final Consumer<Graph> removeGraph = g -> 
g.find().forEach(g::delete);
 
     @Override
     public void addGraph(final Node graphName, final Graph graph) {
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java
index fd2a5f261e..370d93bf49 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java
@@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
+import java.util.function.Function;
 
 import org.apache.jena.atlas.lib.Lib;
 import org.apache.jena.atlas.logging.Log;
@@ -389,12 +390,6 @@ public class Context {
         context.clear();
     }
 
-    /** Atomic compute. */
-    @SuppressWarnings("unchecked")
-    public <V> V compute(Symbol key, BiFunction<Symbol, Object, ? extends V> 
remappingFunction) {
-        return (V)context.compute(key, remappingFunction);
-    }
-
     @Override
     public String toString() {
         String x = "";
@@ -439,13 +434,36 @@ public class Context {
         }
     }
 
+    /** Atomic compute. */
+    @SuppressWarnings("unchecked")
+    public <V> V compute(Symbol key, BiFunction<Symbol, Object, ? extends V> 
remappingFunction) {
+        Object obj = context.compute(key, remappingFunction);
+        return (V)obj;
+    }
+
+    /** Atomic computeIfAbsent. */
+    @SuppressWarnings("unchecked")
+    public <V> V computeIfAbsent(Symbol key, Function<Symbol, ? extends V> 
mappingFunction) {
+        Object obj = context.computeIfAbsent(key, mappingFunction);
+        return (V)obj;
+    }
+
+    /** Atomic computeIfPresent. */
+    @SuppressWarnings("unchecked")
+    public <V> V computeIfPresent(Symbol key, BiFunction<Symbol, Object, V> 
remappingFunction) {
+        Object obj = context.computeIfPresent(key, remappingFunction);
+        return (V)obj;
+    }
+
+    /** Get the context's cancel signal. Create and set one if needed. Context 
must not be null. */
     public static AtomicBoolean getOrSetCancelSignal(Context context) {
-        AtomicBoolean cancelSignal = getCancelSignal(context);
-        if (cancelSignal == null) {
-            cancelSignal = new AtomicBoolean(false);
-            context.set(ARQConstants.symCancelQuery, cancelSignal);
+        try {
+            AtomicBoolean result = 
context.computeIfAbsent(ARQConstants.symCancelQuery, sym -> new 
AtomicBoolean(false));
+            return result;
+        } catch (ClassCastException ex) {
+            Log.error(Context.class, "Class cast exception: Expected 
AtomicBoolean for cancel control: "+ex.getMessage());
+            return null;
         }
-        return cancelSignal;
     }
 
     /** Merge an outer (defaults to the system global context)
diff --git 
a/jena-arq/src/test/java/org/apache/jena/rdfs/TestDatasetGraphRDFS.java 
b/jena-arq/src/test/java/org/apache/jena/rdfs/TestDatasetGraphRDFS.java
index 574bce81a6..095a366c3e 100644
--- a/jena-arq/src/test/java/org/apache/jena/rdfs/TestDatasetGraphRDFS.java
+++ b/jena-arq/src/test/java/org/apache/jena/rdfs/TestDatasetGraphRDFS.java
@@ -24,6 +24,7 @@ import static org.apache.jena.rdfs.LibTestRDFS.node;
 import static org.apache.jena.rdfs.engine.ConstRDFS.rdfType;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.PrintStream;
@@ -77,6 +78,12 @@ public class TestDatasetGraphRDFS {
         Iter.consume(iter3);
     }
 
+    @Test public void dsg_find_all() {
+        List<Quad> baseQuads = Iter.toList(dsg.getBase().find());
+        List<Quad> inferredQuads = Iter.toList(dsg.find());
+        assertNotEquals(baseQuads, inferredQuads);
+    }
+
     @Test public void dsg_find_graph() {
         List<Quad> x = test(node("g"), node("a"), rdfType, null);
         assertTrue(hasNG(x, node("g"))) ;
diff --git 
a/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/BenchmarkSpatialQueries.java
 
b/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/BenchmarkSpatialQueries.java
new file mode 100644
index 0000000000..476fa8b613
--- /dev/null
+++ 
b/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/BenchmarkSpatialQueries.java
@@ -0,0 +1,242 @@
+/*
+ * 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.query;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.jts.CustomGeometryFactory;
+import org.apache.jena.geosparql.implementation.vocabulary.Geo;
+import org.apache.jena.geosparql.spatial.index.v2.GeometryGenerator;
+import 
org.apache.jena.geosparql.spatial.index.v2.GeometryGenerator.GeometryType;
+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.riot.RDFDataMgr;
+import org.apache.jena.riot.RDFFormat;
+import org.apache.jena.sparql.graph.GraphFactory;
+import org.apache.jena.system.G;
+import org.apache.jena.vocabulary.RDF;
+import org.locationtech.jts.geom.Envelope;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.util.AffineTransformation;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.results.format.ResultFormatType;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.ChainedOptionsBuilder;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.openjdk.jmh.runner.options.TimeValue;
+
+/**
+ * Benchmarking of spatial queries against test data.
+ */
+@State(Scope.Benchmark)
+public class BenchmarkSpatialQueries {
+
+    private static Map<String, String> idToQuery = new LinkedHashMap<>();
+
+    private Node featureNode = 
NodeFactory.createURI("urn:test:geosparql:feature1");
+    private Node geometryNode = 
NodeFactory.createURI("urn:test:geosparql:geometry1");
+
+    private static final String q1 = """
+        PREFIX geo: <http://www.opengis.net/ont/geosparql#>
+        PREFIX ogcsf: <http://www.opengis.net/ont/sf#>
+
+        SELECT *
+        WHERE {
+          ?s geo:sfWithin <urn:test:geosparql:geometry1> .
+        }
+        """;
+
+    private static final String q2 = """
+        PREFIX geo: <http://www.opengis.net/ont/geosparql#>
+        PREFIX ogcsf: <http://www.opengis.net/ont/sf#>
+
+        SELECT *
+        WHERE {
+          ?s a ogcsf:Point .
+          ?s geo:sfWithin <urn:test:geosparql:geometry1> .
+        }
+        """;
+
+    static {
+        idToQuery.put("q1", q1);
+        idToQuery.put("q2", q2);
+    }
+
+    /** Essentially the size of the data. One geometry mix includes every WKT 
geometry type once (with different coordinates). */
+    @Param({
+        "10000",
+    })
+    public long p1_geoMixes;
+
+    @Param({
+        "q1",
+        "q2",
+    })
+    public String p2_queryId;
+
+    @Param({
+        "off",
+        "virtual",
+        "materialized"
+    })
+    public String p3_inferences;
+
+    @Param({
+        "false",
+        "true"
+    })
+    public boolean p4_index;
+
+    @Param({
+        "current",
+        "5.5.0"
+    })
+    public String p5_jenaVersion;
+
+    private SpatialQueryTask task;
+
+    @Benchmark
+    public void run() throws Exception {
+        long count = task.exec();
+        if (true) {
+            System.out.println("Counted: " + count);
+        }
+    }
+
+    private static GeometryWrapper toWrapperWkt(Geometry geometry) {
+        GeometryWrapper result = new GeometryWrapper(geometry, Geo.WKT);
+        return result;
+    }
+
+    @Setup(Level.Trial)
+    public void setupTrial() throws Exception {
+        Envelope dataBbox = new Envelope(-175, 175, -85, 85);
+        Map<GeometryType, Number> config = 
GeometryGenerator.createConfig(p1_geoMixes);
+        Graph graph = GraphFactory.createDefaultGraph();
+        GeometryGenerator.generateGraph(graph, dataBbox, config);
+
+        // Build a search-bbox by scaling the data-generation-bbox down.
+        Geometry dataBboxGeom = 
CustomGeometryFactory.theInstance().toGeometry(dataBbox);
+        double x = dataBboxGeom.getCentroid().getX();
+        double y = dataBboxGeom.getCentroid().getY();
+        Geometry searchBboxGeom = AffineTransformation.scaleInstance(0.25, 
0.25, x, y).transform(dataBboxGeom);
+
+        // Add search bbox and feature/resource to the benchmark data.
+        Node searchBboxNode = toWrapperWkt(searchBboxGeom).asNode();
+        graph.add(featureNode, Geo.HAS_GEOMETRY_NODE, geometryNode);
+        graph.add(geometryNode, Geo.AS_WKT_NODE, searchBboxNode);
+
+        // Post process test data:
+        // - Add "geom a Point" triples to geometry resources with a Point WKT 
literal.
+        // - Add explicit Geometry type to all geometry resources (required by 
jena-geosparql 5.5.0 and earlier).
+        Node Point = 
NodeFactory.createURI("http://www.opengis.net/ont/sf#Point";);
+        Graph extraGraph = GraphFactory.createDefaultGraph();
+        try (Stream<Triple> stream = graph.stream(null, Geo.AS_WKT_NODE, 
null)) {
+            stream.forEach(t -> {
+                GeometryWrapper gw = GeometryWrapper.extract(t.getObject());
+                String geoType = gw.getGeometryType();
+                if (geoType.equals("Point")) {
+                    extraGraph.add(t.getSubject(), RDF.Nodes.type, Point);
+                }
+
+                extraGraph.add(t.getSubject(), RDF.Nodes.type, 
Geo.GEOMETRY_NODE);
+            });
+        }
+        G.addInto(graph, extraGraph);
+
+        String data;
+        RDFFormat fmt = RDFFormat.TURTLE_PRETTY;
+        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+            RDFDataMgr.write(out, graph, fmt);
+            out.flush();
+            data = new String(out.toByteArray(), StandardCharsets.UTF_8);
+        }
+
+        task = switch (p5_jenaVersion) {
+        case "current" -> new SpatialQueryTaskCurrent();
+        case "5.5.0" -> new SpatialQueryTask550();
+        default -> throw new RuntimeException("No task registered for this 
jena version:" + p5_jenaVersion);
+        };
+
+        task.setData(data);
+
+        switch (p3_inferences) {
+        case "off": task.setInferenceMode(false, false); break;
+        case "virtual": task.setInferenceMode(true, false); break;
+        case "materialized": task.setInferenceMode(true, true); break;
+        default:
+            throw new IllegalArgumentException("Unsupported inference mode: " 
+ p3_inferences);
+        }
+
+        task.setIndex(p4_index);
+
+        String queryString = idToQuery.get(p2_queryId);
+        task.setQuery(queryString);
+    }
+
+    @TearDown(Level.Trial)
+    public void tearDownTrial() throws Exception {
+    }
+
+    public static ChainedOptionsBuilder getDefaults(Class<?> c) {
+        return new OptionsBuilder()
+                // Specify which benchmarks to run.
+                // You can be more specific if you'd like to run only one 
benchmark per test.
+                .include(c.getName())
+                // Set the following options as needed
+                .mode(Mode.AverageTime)
+                .timeUnit(TimeUnit.SECONDS)
+                .warmupTime(TimeValue.NONE)
+                .warmupIterations(5)
+                .measurementIterations(5)
+                .measurementTime(TimeValue.NONE)
+                .threads(1)
+                .forks(1)
+                .shouldFailOnError(true)
+                .shouldDoGC(true)
+                //.jvmArgs("-XX:+UnlockDiagnosticVMOptions", 
"-XX:+PrintInlining")
+                .jvmArgs("-Xmx8G")
+                //.addProfiler(WinPerfAsmProfiler.class)
+                .resultFormat(ResultFormatType.JSON)
+                .result(c.getSimpleName() + "_" + 
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + 
".json");
+    }
+
+    public static void main(String[] args) throws RunnerException {
+        Options opt = getDefaults(BenchmarkSpatialQueries.class).build();
+        new Runner(opt).run();
+    }
+}
diff --git 
a/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/SpatialQueryTask.java
 
b/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/SpatialQueryTask.java
new file mode 100644
index 0000000000..0fe6e4dde0
--- /dev/null
+++ 
b/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/SpatialQueryTask.java
@@ -0,0 +1,27 @@
+/*
+ * 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.query;
+
+public interface SpatialQueryTask {
+    void setData(String trigString) throws Exception;
+    void setInferenceMode(boolean enableInferences, boolean materialize) 
throws Exception;
+    void setQuery(String queryString) throws Exception;
+    void setIndex(boolean isEnabled);
+    long exec();
+}
diff --git 
a/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/SpatialQueryTask550.java
 
b/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/SpatialQueryTask550.java
new file mode 100644
index 0000000000..77bb1a6c6a
--- /dev/null
+++ 
b/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/SpatialQueryTask550.java
@@ -0,0 +1,95 @@
+/*
+ * 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.query;
+
+import java.util.stream.Stream;
+
+import org.apache.shadedJena550.geosparql.configuration.GeoSPARQLOperations;
+import org.apache.shadedJena550.geosparql.spatial.SpatialIndexException;
+import org.apache.shadedJena550.geosparql.spatial.index.v2.SpatialIndexLib;
+import org.apache.shadedJena550.graph.Graph;
+import org.apache.shadedJena550.rdfs.RDFSFactory;
+import org.apache.shadedJena550.riot.Lang;
+import org.apache.shadedJena550.riot.RDFParser;
+import org.apache.shadedJena550.sparql.core.DatasetGraph;
+import org.apache.shadedJena550.sparql.core.DatasetGraphFactory;
+import org.apache.shadedJena550.sparql.core.Quad;
+import org.apache.shadedJena550.sparql.exec.QueryExec;
+import org.apache.shadedJena550.sparql.exec.RowSetOps;
+
+public class SpatialQueryTask550
+    implements SpatialQueryTask
+{
+    private DatasetGraph baseDsg = null;
+    private DatasetGraph effectiveDsg = null;
+    private String query;
+
+    @Override
+    public void setData(String trigString) throws Exception {
+        baseDsg = 
RDFParser.create().fromString(trigString).lang(Lang.TRIG).toDatasetGraph();
+    }
+
+    @Override
+    public void setQuery(String queryString) throws Exception {
+        this.query = queryString;
+    }
+
+    @Override
+    public void setInferenceMode(boolean enableInferences, boolean 
materialize) {
+        if (enableInferences) {
+            Graph vocab = GeoSPARQLOperations.loadGeoSPARQLSchema().getGraph();
+            DatasetGraph virtualDsg = RDFSFactory.datasetRDFS(baseDsg, vocab);
+            if (materialize) {
+                effectiveDsg = DatasetGraphFactory.create();
+
+                // Bugged in 5.5.0 because find() is not overridden to yield 
inferences:
+                // effectiveDsg.addAll(virtualDsg);
+
+                try (Stream<Quad> stream = virtualDsg.stream(null, null, null, 
null)) {
+                    stream.forEach(effectiveDsg::add);
+                }
+            } else {
+                effectiveDsg = virtualDsg;
+            }
+        } else {
+            effectiveDsg = baseDsg;
+        }
+
+        // RDFDataMgr.write(System.err, effectiveDsg, RDFFormat.TRIG_PRETTY);
+    }
+
+    @Override
+    public void setIndex(boolean isEnabled) {
+        if (isEnabled) {
+            try {
+                SpatialIndexLib.buildSpatialIndex(effectiveDsg);
+            } catch (SpatialIndexException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @Override
+    public long exec() {
+        try (QueryExec qe = 
QueryExec.dataset(effectiveDsg).query(query).build()) {
+            long count = RowSetOps.count(qe.select());
+            return count;
+        }
+    }
+}
diff --git 
a/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/SpatialQueryTaskCurrent.java
 
b/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/SpatialQueryTaskCurrent.java
new file mode 100644
index 0000000000..e90f64fca9
--- /dev/null
+++ 
b/jena-benchmarks/jena-benchmarks-jmh/src/test/java/org/apache/jena/geosparql/query/SpatialQueryTaskCurrent.java
@@ -0,0 +1,84 @@
+/*
+ * 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.query;
+
+import org.apache.jena.geosparql.configuration.GeoSPARQLOperations;
+import org.apache.jena.geosparql.spatial.SpatialIndexException;
+import org.apache.jena.geosparql.spatial.index.v2.SpatialIndexLib;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.rdfs.RDFSFactory;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFParser;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphFactory;
+import org.apache.jena.sparql.exec.QueryExec;
+import org.apache.jena.sparql.exec.RowSetOps;
+
+public class SpatialQueryTaskCurrent
+    implements SpatialQueryTask
+{
+    private DatasetGraph baseDsg = null;
+    private DatasetGraph effectiveDsg = null;
+    private String query;
+
+    @Override
+    public void setData(String ttlString) throws Exception {
+        baseDsg = 
RDFParser.create().fromString(ttlString).lang(Lang.TRIG).toDatasetGraph();
+    }
+
+    @Override
+    public void setQuery(String queryString) throws Exception {
+        this.query = queryString;
+    }
+
+    @Override
+    public void setInferenceMode(boolean enableInferences, boolean 
materialize) {
+        if (enableInferences) {
+            Graph vocab = GeoSPARQLOperations.loadGeoSPARQLSchema().getGraph();
+            DatasetGraph virtualDsg = RDFSFactory.datasetRDFS(baseDsg, vocab);
+            if (materialize) {
+                effectiveDsg = DatasetGraphFactory.create();
+                effectiveDsg.addAll(virtualDsg);
+            } else {
+                effectiveDsg = virtualDsg;
+            }
+        } else {
+            effectiveDsg = baseDsg;
+        }
+    }
+
+    @Override
+    public void setIndex(boolean isEnabled) {
+        if (isEnabled) {
+            try {
+                SpatialIndexLib.buildSpatialIndex(effectiveDsg);
+            } catch (SpatialIndexException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @Override
+    public long exec() {
+        try (QueryExec qe = 
QueryExec.dataset(effectiveDsg).query(query).build()) {
+            long count = RowSetOps.count(qe.select());
+            return count;
+        }
+    }
+}
diff --git 
a/jena-fuseki2/jena-fuseki-mod-geosparql/src/main/java/org/apache/jena/fuseki/mod/geosparql/SpatialIndexerService.java
 
b/jena-fuseki2/jena-fuseki-mod-geosparql/src/main/java/org/apache/jena/fuseki/mod/geosparql/SpatialIndexerService.java
index 87eaa33aa1..9787f1dba7 100644
--- 
a/jena-fuseki2/jena-fuseki-mod-geosparql/src/main/java/org/apache/jena/fuseki/mod/geosparql/SpatialIndexerService.java
+++ 
b/jena-fuseki2/jena-fuseki-mod-geosparql/src/main/java/org/apache/jena/fuseki/mod/geosparql/SpatialIndexerService.java
@@ -120,18 +120,26 @@ public class SpatialIndexerService extends BaseActionREST 
{
 
     public SpatialIndexerService() {}
 
-    private static <T, C extends Collection<Node>> Set<Node> 
extractGraphsFromRequest(DatasetGraph dsg, HttpAction action) {
+    /**
+     * Extract the explicit set of graphs from the action w.r.t. the dataset 
graph.
+     *
+     * @param dsg The dataset graph.
+     * @param action The HTTP action.
+     * @param emptySelectionToAllGraphs Select all graphs if the request 
specifies an empty selection of graphs.
+     * @return The explicit set of graphs w.r.t. the dataset graph.
+     */
+    private static Set<Node> extractGraphsFromRequest(DatasetGraph dsg, 
HttpAction action, boolean emptySelectionToAllGraphs) {
         String uris = action.getRequest().getParameter(HttpNames.paramGraph);
         Collection<String> strs;
         if (uris == null || uris.isBlank()) {
             strs = List.of(Quad.defaultGraphIRI.toString(), 
Quad.unionGraph.toString());
         } else {
-            TypeToken<List<String>> typeToken = new 
TypeToken<List<String>>(){};
+            TypeToken<List<String>> typeToken = new TypeToken<>(){};
             strs = gsonForSse.fromJson(uris, typeToken);
         }
         List<Node> rawGraphNodes = 
strs.stream().map(NodeFactory::createURI).distinct().toList();
         // If the set of specified graphs is empty then index all.
-        if (rawGraphNodes.isEmpty()) {
+        if (rawGraphNodes.isEmpty() && emptySelectionToAllGraphs) {
             rawGraphNodes = List.of(Quad.defaultGraphIRI, Quad.unionGraph);
         }
 
@@ -453,7 +461,7 @@ public class SpatialIndexerService extends BaseActionREST {
 
         long graphCount = indexComputation.getGraphNodes().size();
 
-        TaskListener<BasicTask> taskListener = new TaskListener<BasicTask>() {
+        TaskListener<BasicTask> taskListener = new TaskListener<>() {
             @Override
             public void onStateChange(BasicTask task) {
                 switch (task.getTaskState()) {
@@ -475,6 +483,7 @@ public class SpatialIndexerService extends BaseActionREST {
                     if (logger.isInfoEnabled()) {
                         logger.info("Indexing task of {} graphs terminated.", 
graphCount);
                     }
+                    break;
                 }
                 default:
                     break;
@@ -496,6 +505,8 @@ public class SpatialIndexerService extends BaseActionREST {
             ServletOps.error(HttpSC.SERVICE_UNAVAILABLE_503, msg);
         } else {
             boolean isReplaceMode = isReplaceMode(action);
+            boolean isUpdateMode = !isReplaceMode;
+
             int threadCount = getThreadCount(action);
 
             // Only SpatialIndexPerGraph can be updated.
@@ -503,7 +514,6 @@ public class SpatialIndexerService extends BaseActionREST {
             // If not then raise an exception
             // that informs that only replace mode can be used in this 
situation.
             if (!(index instanceof SpatialIndexPerGraph)) {
-                boolean isUpdateMode = !isReplaceMode;
                 if (isUpdateMode) {
                     throw new RuntimeException("Cannot update existing spatial 
index because its type is unsupported. Consider replacing the index.");
                 }
@@ -516,7 +526,7 @@ public class SpatialIndexerService extends BaseActionREST {
 
             String srsURI = index.getSrsInfo().getSrsURI();
 
-            List<Node> graphNodes = new ArrayList<>(Txn.calculateRead(dsg, () 
-> extractGraphsFromRequest(dsg, action)));
+            List<Node> graphNodes = new ArrayList<>(Txn.calculateRead(dsg, () 
-> extractGraphsFromRequest(dsg, action, isUpdateMode)));
             SpatialIndexerComputation task = new 
SpatialIndexerComputation(dsg, srsURI, graphNodes, threadCount);
 
             action.log.info(String.format("[%d] spatial index: computation 
request accepted.", action.id));
diff --git 
a/jena-fuseki2/jena-fuseki-mod-geosparql/src/main/resources/spatial-indexer/index.html
 
b/jena-fuseki2/jena-fuseki-mod-geosparql/src/main/resources/spatial-indexer/index.html
index be6caaacfb..fdebc8dd55 100644
--- 
a/jena-fuseki2/jena-fuseki-mod-geosparql/src/main/resources/spatial-indexer/index.html
+++ 
b/jena-fuseki2/jena-fuseki-mod-geosparql/src/main/resources/spatial-indexer/index.html
@@ -218,8 +218,9 @@
         }
 
         function updateApplyButtonLabel() {
+            const replaceMode = replaceCb.checked;
             const selectedGraphs = document.querySelectorAll('#graph-list 
input:checked');
-            applyBtn.textContent = selectedGraphs.length === 0 ? 'Index all 
graphs' : `Index ${selectedGraphs.length} graphs`;
+            applyBtn.textContent = (!replaceMode && selectedGraphs.length === 
0) ? 'Index all graphs' : `Index ${selectedGraphs.length} graphs`;
         }
 
         document.getElementById('filter').addEventListener('input', function 
() {
@@ -303,6 +304,10 @@
             updateApplyButtonLabel();
         });
 
+        replaceCb.addEventListener("change", function () {
+            updateApplyButtonLabel();
+        });
+
         function updateStatus() {
             updateCancelButton();
             updateStatusMessage();
diff --git 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/GenericGeometryPropertyFunction.java
 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/GenericGeometryPropertyFunction.java
index b76e5c9b32..91d5b4f019 100644
--- 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/GenericGeometryPropertyFunction.java
+++ 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geo/topological/GenericGeometryPropertyFunction.java
@@ -19,7 +19,7 @@ package org.apache.jena.geosparql.geo.topological;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
-import org.apache.jena.geosparql.implementation.vocabulary.Geo;
+import org.apache.jena.geosparql.implementation.access.AccessGeoSPARQL;
 import org.apache.jena.graph.Graph;
 import org.apache.jena.graph.Node;
 import org.apache.jena.graph.Triple;
@@ -28,15 +28,15 @@ 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.QueryIter;
 import org.apache.jena.sparql.engine.iterator.QueryIterNullIterator;
+import org.apache.jena.sparql.engine.iterator.QueryIterPlainWrapper;
 import org.apache.jena.sparql.engine.iterator.QueryIterSingleton;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
 import org.apache.jena.sparql.pfunction.PFuncSimple;
 import org.apache.jena.system.G;
 import org.apache.jena.util.iterator.ExtendedIterator;
-import org.apache.jena.vocabulary.RDF;
 
 /**
  *
@@ -62,7 +62,6 @@ public abstract class GenericGeometryPropertyFunction extends 
PFuncSimple {
             //Subject unbound and object bound.
             return subjectUnbound(binding, subject, predicate, object, 
execCxt);
         }
-
     }
 
     protected Node getGeometryLiteral(Node subject, Node predicate, Graph 
graph) throws ExprEvalException {
@@ -73,15 +72,7 @@ public abstract class GenericGeometryPropertyFunction 
extends PFuncSimple {
                 return G.getSP(graph, subject, predicate);
 
             //Check that the Geometry has a serialisation to use.
-            Node geomLiteral = G.getSP(graph, subject, 
Geo.HAS_SERIALIZATION_NODE);
-
-            // If hasSerialization not found then check asWKT and asGML.
-            if (geomLiteral == null) {
-                geomLiteral = G.getSP(graph, subject, Geo.AS_WKT_NODE);
-                if (geomLiteral == null)
-                    geomLiteral = G.getSP(graph, subject, Geo.AS_GML_NODE);
-            }
-
+            Node geomLiteral = AccessGeoSPARQL.getGeoLiteral(graph, subject);
             if (geomLiteral != null) {
                 GeometryWrapper geometryWrapper = 
GeometryWrapper.extract(geomLiteral);
                 NodeValue predicateResult = applyPredicate(geometryWrapper);
@@ -111,25 +102,23 @@ public abstract class GenericGeometryPropertyFunction 
extends PFuncSimple {
     }
 
     private QueryIterator subjectUnbound(Binding binding, Node subject, Node 
predicate, Node object, ExecutionContext execCxt) {
-        QueryIterConcat queryIterConcat = new QueryIterConcat(execCxt);
-
         Graph graph = execCxt.getActiveGraph();
 
-        ExtendedIterator<Triple> subjectTriples = graph.find(null, 
RDF.type.asNode(), Geo.GEOMETRY_NODE);
-
+        ExtendedIterator<Triple> subjectTriples = 
AccessGeoSPARQL.findSpecificGeoLiterals(graph);
         Var subjectVar = Var.alloc(subject.getName());
-        while (subjectTriples.hasNext()) {
-            Triple subjectTriple = subjectTriples.next();
-            Binding newBind = BindingFactory.binding(binding, subjectVar, 
subjectTriple.getSubject());
-            QueryIterator queryIter = bothBound(newBind, 
subjectTriple.getSubject(), predicate, object, execCxt);
-            queryIterConcat.add(queryIter);
-        }
+        ExtendedIterator<Binding> iterator = subjectTriples
+                .mapWith(Triple::getSubject)
+                .mapWith(node -> BindingFactory.binding(binding, subjectVar, 
node));
 
-        return queryIterConcat;
+        QueryIter queryIter = QueryIter.flatMap(
+                QueryIterPlainWrapper.create(iterator, execCxt),
+                b -> bothBound(b, b.get(subjectVar), predicate, object, 
execCxt),
+                execCxt);
+
+        return queryIter;
     }
 
     private QueryIterator objectUnbound(Binding binding, Node subject, Node 
predicate, Node object, ExecutionContext execCxt) {
-
         Graph graph = execCxt.getActiveGraph();
         Node geometryLiteral = getGeometryLiteral(subject, predicate, graph);
 
@@ -141,21 +130,20 @@ public abstract class GenericGeometryPropertyFunction 
extends PFuncSimple {
     }
 
     private QueryIterator bothUnbound(Binding binding, Node subject, Node 
predicate, Node object, ExecutionContext execCxt) {
-        QueryIterConcat queryIterConcat = new QueryIterConcat(execCxt);
-
         Graph graph = execCxt.getActiveGraph();
 
-        ExtendedIterator<Triple> subjectTriples = graph.find(null, 
RDF.type.asNode(), Geo.GEOMETRY_NODE);
-
+        ExtendedIterator<Triple> subjectTriples = 
AccessGeoSPARQL.findSpecificGeoLiterals(graph);
         Var subjectVar = Var.alloc(subject.getName());
-        while (subjectTriples.hasNext()) {
-            Triple subjectTriple = subjectTriples.next();
-            Binding newBind = BindingFactory.binding(binding, subjectVar, 
subjectTriple.getSubject());
-            QueryIterator queryIter = objectUnbound(newBind, 
subjectTriple.getSubject(), predicate, object, execCxt);
-            queryIterConcat.add(queryIter);
-        }
+        ExtendedIterator<Binding> iterator = subjectTriples
+                .mapWith(Triple::getSubject)
+                .mapWith(node -> BindingFactory.binding(binding, subjectVar, 
node));
+
+        QueryIter queryIter = QueryIter.flatMap(
+                QueryIterPlainWrapper.create(iterator, execCxt),
+                b -> objectUnbound(b, b.get(subjectVar), predicate, object, 
execCxt),
+                execCxt);
 
-        return queryIterConcat;
+        return queryIter;
     }
 
 }
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 c1185a71ad..9b913abfc9 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
@@ -23,11 +23,10 @@ import java.util.List;
 import org.apache.jena.atlas.iterator.Iter;
 import org.apache.jena.geosparql.geof.topological.GenericFilterFunction;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.access.AccessGeoSPARQL;
+import org.apache.jena.geosparql.implementation.access.AccessWGS84;
 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.geosparql.spatial.index.v2.SpatialIndexLib;
 import org.apache.jena.graph.Graph;
 import org.apache.jena.graph.Node;
@@ -47,7 +46,6 @@ import org.apache.jena.sparql.pfunction.PFuncSimple;
 import org.apache.jena.sparql.util.FmtUtils;
 import org.apache.jena.system.G;
 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;
@@ -68,38 +66,42 @@ public abstract class GenericPropertyFunction extends 
PFuncSimple {
 
     @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);
+        // These Property Functions accept literals as objects as an extension 
over GeoSPARQL 1.0.
+
+        QueryRewriteIndex queryRewriteIndex = 
QueryRewriteIndex.getOrCreate(execCxt);
+        SpatialIndex spatialIndex = 
SpatialIndexLib.getSpatialIndex(execCxt.getContext());
+
+        if (subject.isConcrete()) {
+            if (object.isConcrete()) {
+                //Both are bound.
+                return bothBound(binding, subject, predicate, object, execCxt, 
queryRewriteIndex);
+            } else {
+                //One bound and one unbound.
+                return oneBoundChecked(binding, subject, predicate, object, 
true, execCxt, spatialIndex, queryRewriteIndex);
+            }
         } else {
-            //One bound and one unbound.
-            return oneBound(binding, subject, predicate, object, execCxt);
+            if (object.isConcrete()) {
+                //One bound and one unbound.
+                return oneBoundChecked(binding, object, predicate, subject, 
false, execCxt, spatialIndex, queryRewriteIndex);
+            } else {
+                //Both are unbound.
+                return bothUnbound(binding, subject, predicate, object, 
execCxt, spatialIndex, queryRewriteIndex);
+            }
         }
     }
 
-    private QueryIterator bothBound(Binding binding, boolean isSubjectBound, 
Node subject, Node predicate, Node object, ExecutionContext execCxt) {
+    private QueryIterator bothBound(Binding binding, boolean isSubjectBound, 
Node subject, Node predicate, Node object, ExecutionContext execCxt, 
QueryRewriteIndex queryRewriteIndex) {
         QueryIterator iter = isSubjectBound
-                ? bothBound(binding, subject, predicate, object, execCxt)
-                : bothBound(binding, object, predicate, subject, execCxt);
+                ? bothBound(binding, subject, predicate, object, execCxt, 
queryRewriteIndex)
+                : bothBound(binding, object, predicate, subject, execCxt, 
queryRewriteIndex);
         return iter;
     }
 
-    private QueryIterator bothBound(Binding binding, Node subject, Node 
predicate, Node object, ExecutionContext execCxt) {
+    private QueryIterator bothBound(Binding binding, Node subject, Node 
predicate, Node object, ExecutionContext execCxt, QueryRewriteIndex 
queryRewriteIndex) {
         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.
+            //Filter function test succeeded so retain binding.
             return QueryIterSingleton.create(binding, execCxt);
         } else {
             //Filter function test failed so null result.
@@ -107,103 +109,83 @@ public abstract class GenericPropertyFunction extends 
PFuncSimple {
         }
     }
 
-    private QueryIterator bothUnbound(Binding binding, Node subject, Node 
predicate, Node object, ExecutionContext execCxt) {
+    private QueryIterator bothUnbound(Binding binding, Node subject, Node 
predicate, Node object, ExecutionContext execCxt, SpatialIndex spatialIndex, 
QueryRewriteIndex queryRewriteIndex) {
         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> spatialTriples = findSpatialTriples(graph);
-        ExtendedIterator<Binding> iterator = spatialTriples
-            .mapWith(Triple::getSubject)
+        ExtendedIterator<Binding> iterator = findSpatialObjects(graph)
             .mapWith(node -> BindingFactory.binding(binding, subjectVar, 
node));
 
         QueryIter queryIter = QueryIter.flatMap(
             QueryIterPlainWrapper.create(iterator, execCxt),
-            b -> oneBound(b, b.get(subjectVar), predicate, object, execCxt),
+            b -> oneBound(graph, b, b.get(subjectVar), predicate, object, 
true, execCxt, spatialIndex, queryRewriteIndex),
             execCxt
         );
         return queryIter;
     }
 
-    private QueryIterator oneBound(Binding binding, Node subject, Node 
predicate, Node object, ExecutionContext execCxt) {
-
+    /** Validate the bound node for whether it is a literal or spatial object. 
*/
+    private QueryIterator oneBoundChecked(Binding binding, Node boundNode, 
Node predicate, Node unboundNode, boolean isSubjectBound, ExecutionContext 
execCxt, SpatialIndex spatialIndex, QueryRewriteIndex queryRewriteIndex) {
         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);
-            }
+        // If the bound node can't match in the first place then bail out 
early.
+        // Otherwise, the whole unbound side would be scanned and tested 
against the bound node.
+        if (!(boundNode.isLiteral() || 
AccessGeoSPARQL.isSpatialObjectByProperties(graph, boundNode))) {
+            return QueryIterNullIterator.create(execCxt);
         }
 
-        boolean isSpatialIndex = SpatialIndexLib.isDefined(execCxt);
+        return oneBound(graph, binding, boundNode, predicate, unboundNode, 
isSubjectBound, execCxt, spatialIndex, queryRewriteIndex);
+    }
+
+    private QueryIterator oneBound(Graph graph, Binding binding, Node 
boundNode, Node predicate, Node unboundNode, boolean isSubjectBound, 
ExecutionContext execCxt, SpatialIndex spatialIndex, QueryRewriteIndex 
queryRewriteIndex) {
         QueryIterator result;
-        if (!isSpatialIndex || filterFunction.isDisjoint() || 
filterFunction.isDisconnected()) {
+        if (spatialIndex == null || filterFunction.isDisjoint() || 
filterFunction.isDisconnected()) {
             //Disjointed so retrieve all cases.
-            result = findAll(graph, boundNode, unboundNode, binding, 
isSubjectBound, predicate, execCxt);
+            result = findAll(graph, binding, boundNode, predicate, 
unboundNode, isSubjectBound, execCxt, queryRewriteIndex);
         } else {
             //Only retrieve those in the spatial index which are within same 
bounding box.
-            result = findIndex(graph, boundNode, unboundNode, binding, 
isSubjectBound, predicate, execCxt);
+            result = findIndex(graph, binding, boundNode, predicate, 
unboundNode, isSubjectBound, execCxt, spatialIndex, queryRewriteIndex);
         }
         return result;
     }
 
-    private QueryIterator findAll(Graph graph, Node boundNode, Node 
unboundNode, Binding binding, boolean isSubjectBound, Node predicate, 
ExecutionContext execCxt) {
+    private QueryIterator findAll(Graph graph, Binding binding, Node 
boundNode, Node predicate, Node unboundNode, boolean isSubjectBound, 
ExecutionContext execCxt, QueryRewriteIndex queryRewriteIndex) {
 
         //Prepare the results.
         Var unboundVar = Var.alloc(unboundNode.getName());
 
         //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 = findSpatialTriples(graph);
-
-        ExtendedIterator<Binding> iterator = spatialTriples
-            .mapWith(Triple::getSubject)
+        ExtendedIterator<Binding> iterator = findSpatialObjects(graph)
             .mapWith(node -> BindingFactory.binding(binding, unboundVar, 
node));
 
         return QueryIter.flatMap(
             QueryIterPlainWrapper.create(iterator, execCxt),
             b -> {
                 Node spatialNode = b.get(unboundVar);
-                QueryIterator iter = bothBound(b, isSubjectBound, boundNode, 
predicate, spatialNode, execCxt);
+                QueryIterator iter = bothBound(b, isSubjectBound, boundNode, 
predicate, spatialNode, execCxt, queryRewriteIndex);
                 return iter;
             },
             execCxt);
     }
 
-    private static ExtendedIterator<Triple> findSpatialTriples(Graph graph) {
-        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);
+    private static ExtendedIterator<Node> findSpatialObjects(Graph graph) {
+        // The found nodes are passed to SpatialObjectGeometryLiteral.retrieve 
which:
+        //   - Filters out all features unless they have a 
geo:hasDefaultGeometry property or wgs84 vocab.
+        //   - Retrieves only a single specific geoLiteral for a geoResource
+        // There would be performance potential by leveraging the triples here 
for retrieve.
+        ExtendedIterator<Triple> result = 
AccessGeoSPARQL.findSpecificGeoLiterals(graph);
+        try {
+            result = 
result.andThen(AccessGeoSPARQL.findDefaultGeoResources(graph));
+            result = 
result.andThen(AccessWGS84.findGeoLiteralsAsTriples(graph, null));
+        } catch (RuntimeException t) {
+            result.close();
+            throw new RuntimeException(t);
         }
-        return spatialTriples;
+        return result.mapWith(Triple::getSubject);
     }
 
-    private QueryIterator findIndex(Graph graph, Node boundNode, Node 
unboundNode, Binding binding, boolean isSubjectBound, Node predicate, 
ExecutionContext execCxt) throws ExprEvalException {
+    private QueryIterator findIndex(Graph graph, Binding binding, Node 
boundNode, Node predicate, Node unboundNode, boolean isSubjectBound, 
ExecutionContext execCxt, SpatialIndex spatialIndex, QueryRewriteIndex 
queryRewriteIndex) throws ExprEvalException {
         try {
             //Prepare for results.
             Var unboundVar = Var.alloc(unboundNode);
@@ -230,7 +212,6 @@ public abstract class GenericPropertyFunction extends 
PFuncSimple {
             Node geometryLiteral = boundGeometryLiteral.getGeometryLiteral();
 
             // Perform the search of the Spatial Index of the Dataset.
-            SpatialIndex spatialIndex = SpatialIndexLib.retrieve(execCxt);
             GeometryWrapper geom = GeometryWrapper.extract(geometryLiteral);
             GeometryWrapper transformedGeom = 
geom.transform(spatialIndex.getSrsInfo());
 
@@ -247,32 +228,33 @@ public abstract class GenericPropertyFunction extends 
PFuncSimple {
                 featureBinding -> {
                     return findByFeature(graph, binding, featureBinding,
                             isSubjectBound, boundNode, predicate, unboundVar,
-                            execCxt, assertedNodes);
+                            execCxt, assertedNodes, queryRewriteIndex);
                 },
                 execCxt);
             queryIterConcat.add(queryIterator);
 
             return queryIterConcat;
-        } catch (MismatchedDimensionException | TransformException | 
FactoryException | SpatialIndexException ex) {
+        } catch (MismatchedDimensionException | TransformException | 
FactoryException ex) {
             throw new ExprEvalException(ex.getMessage() + ": " + 
FmtUtils.stringForNode(boundNode) + ", " + FmtUtils.stringForNode(unboundNode) 
+ ", " + FmtUtils.stringForNode(predicate), ex);
         }
     }
 
     private QueryIterator findByFeature(Graph graph, Binding binding, Binding 
featureBinding,
             boolean isSubjectBound, Node boundNode, Node predicate, Var 
unboundVar,
-            ExecutionContext execCxt, Collection<Node> assertedNodes) {
+            ExecutionContext execCxt, Collection<Node> assertedNodes, 
QueryRewriteIndex queryRewriteIndex) {
 
         Node featureNode = featureBinding.get(unboundVar);
         QueryIterConcat featureIterConcat = new QueryIterConcat(execCxt);
 
         // Check Features directly if not already asserted
         if (!assertedNodes.contains(featureNode)) {
-            QueryIterator tmpIter = bothBound(featureBinding, isSubjectBound, 
boundNode, predicate, featureNode, execCxt);
+            QueryIterator tmpIter = bothBound(featureBinding, isSubjectBound, 
boundNode, predicate, featureNode, execCxt, queryRewriteIndex);
             featureIterConcat.add(tmpIter);
         }
 
         // Also test all Geometry of the Features. All, some or one Geometry 
may have matched.
-        ExtendedIterator<Node> featureGeometries = G.iterSP(graph, 
featureNode, Geo.HAS_GEOMETRY_NODE);
+        // ExtendedIterator<Node> featureGeometries = G.iterSP(graph, 
featureNode, Geo.HAS_GEOMETRY_NODE);
+        ExtendedIterator<Node> featureGeometries = 
AccessGeoSPARQL.findSpecificGeoResources(graph, 
featureNode).mapWith(Triple::getObject);
         QueryIterator geometriesQueryIterator = QueryIterPlainWrapper.create(
             Iter.map(
                 Iter.filter( // omit asserted
@@ -286,7 +268,7 @@ public abstract class GenericPropertyFunction extends 
PFuncSimple {
             geometriesQueryIterator,
             b2 -> {
                 Node geomNode = b2.get(unboundVar);
-                return bothBound(b2, isSubjectBound, boundNode, predicate, 
geomNode, execCxt);
+                return bothBound(b2, isSubjectBound, boundNode, predicate, 
geomNode, execCxt, queryRewriteIndex);
             },
             execCxt);
 
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 42484cec1e..35373e96be 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
@@ -20,16 +20,14 @@ package org.apache.jena.geosparql.geo.topological;
 import java.util.Objects;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
+import org.apache.jena.geosparql.implementation.access.AccessGeoSPARQL;
+import org.apache.jena.geosparql.implementation.access.AccessWGS84;
 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.system.G;
-import org.apache.jena.system.RDFDataException;
-import org.apache.jena.vocabulary.RDF;
 
 /**
  *
@@ -99,13 +97,17 @@ public class SpatialObjectGeometryLiteral {
      * Objects).
      *
      * @param graph
-     * @param targetSpatialObject
+     * @param targetSpatialObject The spatial object.
      * @return SpatialObject/GeometryLiteral pair.
      */
+    // XXX This should return an iterator over all geometry literals rather 
than just picking an arbitrary one.
     protected static final SpatialObjectGeometryLiteral retrieve(Graph graph, 
Node targetSpatialObject) {
+        if (targetSpatialObject == null) {
+            return new SpatialObjectGeometryLiteral(null, null);
+        }
 
-        Node geometry = null;
-        if (targetSpatialObject != null && targetSpatialObject.isLiteral()) {
+        // Special case: Directly supplied literal - must be a geometry.
+        if (targetSpatialObject.isLiteral()) {
             if (targetSpatialObject.getLiteralDatatype() instanceof 
GeometryDatatype) {
                 return new 
SpatialObjectGeometryLiteral(NodeFactory.createBlankNode(), 
targetSpatialObject);
             } else {
@@ -113,42 +115,28 @@ public class SpatialObjectGeometryLiteral {
             }
         }
 
-        if (graph.contains(targetSpatialObject, RDF.type.asNode(), 
Geo.FEATURE_NODE)) {
-            //Target is Feature - find the default Geometry.
-            geometry = G.getSP(graph, targetSpatialObject, 
Geo.HAS_DEFAULT_GEOMETRY_NODE);
+        // If target has a default geometry then it is implicitly a feature.
+        // Use the feature's default geometry if present ...
+        // XXX The original code did not consider geo:hasGeometry here - does 
the spec really only mandate handling of default geometry?
+        Node geometry = G.getSP(graph, targetSpatialObject, 
Geo.HAS_DEFAULT_GEOMETRY_NODE);
 
-        } else if (graph.contains(targetSpatialObject, RDF.type.asNode(), 
Geo.GEOMETRY_NODE)) {
-            //Target is a Geometry.
+        // ... otherwise try to treat the target itself as the geometry 
resource.
+        if (geometry == null) {
             geometry = targetSpatialObject;
         }
 
-        if (geometry != null) {
-            //Find the Geometry Literal of the Geometry.
-            Node literalNode = G.getSP(graph, geometry, 
Geo.HAS_SERIALIZATION_NODE);
-            // If hasSerialization not found then check asWKT.
-            if (literalNode == null)
-                literalNode = G.getSP(graph, geometry, Geo.AS_WKT_NODE);
-            // If asWKT not found then check asGML.
-            if (literalNode == null)
-                literalNode = G.getSP(graph, geometry, Geo.AS_GML_NODE);
-            if (literalNode != null)
-                return new SpatialObjectGeometryLiteral(targetSpatialObject, 
literalNode);
-        } else {
-            //Target is not a Feature or Geometry but could have Geo 
Predicates.
-            if ( graph.contains(targetSpatialObject, 
SpatialExtension.GEO_LAT_NODE, null)
-                    && graph.contains(targetSpatialObject, 
SpatialExtension.GEO_LON_NODE, null)) {
-                try {
-                    //Extract Lat,Lon coordinate.
-                    Node lat = G.getOneSP(graph, targetSpatialObject, 
SpatialExtension.GEO_LAT_NODE);
-                    Node lon = G.getOneSP(graph, targetSpatialObject, 
SpatialExtension.GEO_LON_NODE);
-                    Node latLonGeometryLiteral = ConvertLatLon.toNode(lat, 
lon);
-                    return new 
SpatialObjectGeometryLiteral(targetSpatialObject, latLonGeometryLiteral);
-                } catch ( RDFDataException ex) {
-                    throw new 
DatatypeFormatException(targetSpatialObject.getURI() + " has more than one 
geo:lat or geo:lon property.");
-                }
-            }
+        Node literalNode = AccessGeoSPARQL.getGeoLiteral(graph, geometry);
+
+        // Last resort: Try the legacy WGS84 Geo Positioning vocabulary on the 
targetSpatialObject.
+        if (literalNode == null) {
+            literalNode = AccessWGS84.getGeoLiteral(graph, 
targetSpatialObject);
+        }
+
+        if (literalNode != null) {
+            return new SpatialObjectGeometryLiteral(targetSpatialObject, 
literalNode);
         }
 
+        // No geometry literal found.
         return new SpatialObjectGeometryLiteral(null, null);
     }
 }
diff --git 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/access/AccessGeoSPARQL.java
 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/access/AccessGeoSPARQL.java
new file mode 100644
index 0000000000..fd6bfceaf4
--- /dev/null
+++ 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/access/AccessGeoSPARQL.java
@@ -0,0 +1,232 @@
+/*
+ * 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.implementation.access;
+
+import java.util.Iterator;
+import java.util.Objects;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.geosparql.implementation.vocabulary.Geo;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.system.G;
+import org.apache.jena.util.iterator.ExtendedIterator;
+
+/**
+ * Central place for accessing GeoSparql spatial objects in a {@link Graph}.
+ *
+ * Note: Using the "GeoLiterals" methods on RDF data that do not conform to 
the GeoSparql
+ * specification will return whatever values are present -
+ * regardless of whether those values are valid literals.
+ */
+public class AccessGeoSPARQL {
+    public static boolean isPredicateOfFeature(Node n) {
+        return n.equals(Geo.HAS_GEOMETRY_NODE) || 
n.equals(Geo.HAS_DEFAULT_GEOMETRY_NODE);
+    }
+
+    public static boolean isPredicateOfGeoResource(Node n) {
+        return n.equals(Geo.AS_WKT_NODE) || n.equals(Geo.AS_GML_NODE) || 
n.equals(Geo.HAS_SERIALIZATION_NODE);
+    }
+
+    public static boolean isTripleOfFeature(Triple t) {
+        return isPredicateOfFeature(t.getPredicate());
+    }
+
+    public static boolean isTripleOfGeoResource(Triple t) {
+        return isPredicateOfGeoResource(t.getPredicate());
+    }
+
+    /** True iff the graph contains geometry literals. */
+    public static boolean containsGeoLiterals(Graph graph) {
+        return containsGeoLiterals(graph, null);
+    }
+
+    /** True iff the node has geometry literals. Arguments must not be null. */
+    public static boolean hasGeoLiterals(Graph graph, Node geometry) {
+        Objects.requireNonNull(geometry);
+        return containsGeoLiterals(graph, geometry);
+    }
+
+    /** True if the node has a geometry or default geometry. Arguments must 
not be null. */
+    public static boolean hasGeoResources(Graph graph, Node feature) {
+        Objects.requireNonNull(feature);
+        boolean result =
+            graph.contains(feature, Geo.HAS_DEFAULT_GEOMETRY_NODE, null) ||
+            graph.contains(feature, Geo.HAS_GEOMETRY_NODE, null);
+        return result;
+    }
+
+    /**
+     * True if the node is a geosparql spatial object by the present 
(geometry-related) properties.
+     * A mere "SpatialObject" type does not count.
+     * Arguments must not be null. Wgs84 does not count
+     */
+    public static boolean isSpatialObjectByProperties(Graph graph, Node 
featureOrGeometry) {
+        return hasGeoLiterals(graph, featureOrGeometry) || 
hasGeoResources(graph, featureOrGeometry);
+    }
+
+    /**
+     * Find all triples with geo:hasDefaultGeometry and geo:hasGeometry 
predicates.
+     * If a feature has a default geometry, then this method will omit all its 
(non-default) geometries.
+     */
+    public static ExtendedIterator<Triple> findSpecificGeoResources(Graph 
graph) {
+        // List resources that have a default geometry followed by those that
+        // only have a non-default one.
+        ExtendedIterator<Triple> result = graph.find(null, 
Geo.HAS_DEFAULT_GEOMETRY_NODE, null);
+        try {
+            boolean hasDefaultGeometry = result.hasNext();
+            ExtendedIterator<Triple> it = graph.find(null, 
Geo.HAS_GEOMETRY_NODE, null);
+
+            // No default geometry -> no need to filter.
+            result = hasDefaultGeometry
+                ? result.andThen(it.filterDrop(t -> G.hasProperty(graph, 
t.getSubject(), Geo.HAS_DEFAULT_GEOMETRY_NODE)))
+                : result.andThen(it);
+        } catch (RuntimeException t) {
+            result.close();
+            throw new RuntimeException(t);
+        }
+        return result;
+    }
+
+    public static ExtendedIterator<Triple> findDefaultGeoResources(Graph 
graph) {
+        return graph.find(null, Geo.HAS_DEFAULT_GEOMETRY_NODE, null);
+    }
+
+    public static ExtendedIterator<Triple> findSpecificGeoResources(Graph 
graph, Node feature) {
+        Objects.requireNonNull(feature);
+        ExtendedIterator<Triple> result = graph.find(feature, 
Geo.HAS_DEFAULT_GEOMETRY_NODE, null);
+        try {
+            if (!result.hasNext()) {
+                result.close();
+            }
+            result = graph.find(feature, Geo.HAS_GEOMETRY_NODE, null);
+        } catch (RuntimeException t) {
+            result.close();
+            throw new RuntimeException(t);
+        }
+        return result;
+    }
+
+    /**
+     * Resolve a feature to its set of specific geometries via the following 
chain:
+     * <pre>
+     *   feature -&gt; (geo:hasDefaultGeometry, geo:hasGeometry) -&gt;
+     *     ({geo:asWKT, geo:asGML}, geo:hasSerialization) -&gt; geo-literal.
+     * </pre>
+     *
+     * If a geo:hasDefaultGeometry does not lead to a valid geo-literal there 
is no backtracking to geo:hasGeometry.
+     */
+    public static Iterator<Triple> findSpecificGeoLiteralsByFeature(Graph 
graph, Node feature) {
+        return Iter.flatMap(findSpecificGeoResources(graph, feature),
+            t -> findSpecificGeoLiterals(graph, t.getObject()));
+    }
+
+    /**
+     * Iterate all triples of geometry resources with their most specific 
serialization form.
+     * The specific properties geo:asWKT and geo:asGML take precedence over 
the more general geo:hasSerialization.
+     * This means if a resource has wkt and/or gml then all 
geo:hasSerialization triples will be omitted for it.
+     */
+    public static ExtendedIterator<Triple> findSpecificGeoLiterals(Graph 
graph) {
+        ExtendedIterator<Triple> result = graph.find(null, Geo.AS_WKT_NODE, 
null);
+        try {
+            result = result.andThen(graph.find(null, Geo.AS_GML_NODE, null));
+            // If there is no specific serialization property use the general 
one.
+            if (!result.hasNext()) {
+                result.close();
+                result = graph.find(null, Geo.HAS_SERIALIZATION_NODE, null);
+            } else {
+                // Append more general serializations for those resources that 
lack a specific one.
+                ExtendedIterator<Triple> it = graph.find(null, 
Geo.HAS_SERIALIZATION_NODE, null).filterDrop(t ->
+                    G.hasProperty(graph, t.getSubject(), Geo.AS_WKT_NODE) ||
+                    G.hasProperty(graph, t.getSubject(), Geo.AS_GML_NODE));
+                result = result.andThen(it);
+            }
+        } catch (RuntimeException t) {
+            result.close();
+            throw new RuntimeException(t);
+        }
+        return result;
+    }
+
+    /**
+     * Iterate a given geometry resource's most specific geometry literals.
+     * The geometry resource must not be null.
+     * A specific serialization (WKT, GML) takes precedence over the more 
general hasSerialization property.
+     */
+    public static ExtendedIterator<Triple> findSpecificGeoLiterals(Graph 
graph, Node geometry) {
+        Objects.requireNonNull(geometry);
+        ExtendedIterator<Triple> result = graph.find(geometry, 
Geo.AS_WKT_NODE, null);
+        try {
+            result = result.andThen(graph.find(geometry, Geo.AS_GML_NODE, 
null));
+            if (!result.hasNext()) {
+                result.close();
+                // Fallback to the more generic property.
+                result = graph.find(geometry, Geo.HAS_SERIALIZATION_NODE, 
null);
+            }
+        } catch (RuntimeException t) {
+            result.close();
+            throw new RuntimeException(t);
+        }
+        return result;
+    }
+
+    public static Node getGeoLiteral(Graph graph, Node geometry) {
+        Triple t = getGeoLiteralTriple(graph, geometry);
+        Node n = (t == null) ? null : t.getObject();
+        return n;
+    }
+
+    public static Triple getGeoLiteralTriple(Graph graph, Node geometry) {
+        Objects.requireNonNull(geometry);
+
+        // Find the geometry literal of the geometry resource.
+        Triple t;
+        if ((t = getTripleSP(graph, geometry, Geo.HAS_SERIALIZATION_NODE)) != 
null) {
+            return t;
+        }
+
+        // If hasSerialization not found then check asWKT.
+        if ((t = getTripleSP(graph, geometry, Geo.AS_WKT_NODE)) != null) {
+            return t;
+        }
+
+        // If asWKT not found then check asGML.
+        if ((t = getTripleSP(graph, geometry, Geo.AS_GML_NODE)) != null) {
+            return t;
+        }
+
+        return null;
+    }
+
+    private static Triple getTripleSP(Graph graph, Node s, Node p) {
+        Node o = G.getSP(graph, s, p);
+        Triple t = (o == null) ? null : Triple.create(s, p, o);
+        return t;
+    }
+
+    /** Shared code to test whether a node or graph has serialization 
properties. */
+    private static boolean containsGeoLiterals(Graph graph, Node node) {
+        boolean result =
+            graph.contains(node, Geo.HAS_SERIALIZATION_NODE, null) ||
+            graph.contains(node, Geo.AS_WKT_NODE, null) ||
+            graph.contains(node, Geo.AS_GML_NODE, null);
+        return result;
+    }
+}
diff --git 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/access/AccessWGS84.java
 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/access/AccessWGS84.java
new file mode 100644
index 0000000000..b049c77bd8
--- /dev/null
+++ 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/access/AccessWGS84.java
@@ -0,0 +1,155 @@
+/*
+ * 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.implementation.access;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.datatypes.DatatypeFormatException;
+import org.apache.jena.geosparql.implementation.GeometryWrapper;
+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.Triple;
+import org.apache.jena.system.G;
+import org.apache.jena.system.RDFDataException;
+import org.apache.jena.util.iterator.ExtendedIterator;
+import org.apache.jena.util.iterator.WrappedIterator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Central place for accessing Wgs84 point geometries in a {@link Graph}.
+ */
+public class AccessWGS84 {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    /** True iff the graph contains wgs84:{lat, long} triples.
+     *  True does not imply that there are resources that have both lat AND 
long properties. */
+    public static boolean containsGeoLiteralProperties(Graph graph) {
+        return containsGeoLiteralProperties(graph, null);
+    }
+
+    /** True iff the node has wgs84:{lat, long} triples.
+     *  True does not imply that both lat AND long are present on the node. */
+    public static boolean hasGeoLiteralProperties(Graph graph, Node feature) {
+        Objects.requireNonNull(feature);
+        return containsGeoLiteralProperties(graph, feature);
+    }
+
+    /** For each matching resource, build triples of format 's geo:hasGeometry 
geometryLiteral'. */
+    // XXX geo:hasSerialization might seem a better choice but the original 
jena-geosparql implementation used geo:hasGeometry.
+    public static ExtendedIterator<Triple> findGeoLiteralsAsTriples(Graph 
graph, Node s) {
+        return findGeoLiteralsAsTriples(graph, s, Geo.HAS_GEOMETRY_NODE);
+    }
+
+    /**
+     * For each matching resource and its geometries, create triples of format 
's p geometryLiteral'
+     *
+     * @param graph
+     * @param s The match subject. May be null.
+     * @param p The predicate to use for creating triples. Can be chosen 
freely but must not be null.
+     * @return Iterator of created triples (not obtained from the graph 
directly).
+     */
+    public static ExtendedIterator<Triple> findGeoLiteralsAsTriples(Graph 
graph, Node s, Node p) {
+        return findGeoLiterals(graph, s).mapWith(e -> 
Triple.create(e.getKey(), p, e.getValue().asNode()));
+    }
+
+    /**
+     * For each matching resource, build geometry literals from the cartesian 
product of the WGS84 lat/long properties.
+     * Resources must have both properties, lat and long, to be matched by 
this method.
+     */
+    public static ExtendedIterator<Entry<Node, GeometryWrapper>> 
findGeoLiterals(Graph graph, Node s) {
+        // Warn about multiple lat/lon combinations only at most once per 
graph.
+        boolean enableWarnings = false;
+        boolean[] loggedMultipleLatLons = { false };
+        ExtendedIterator<Triple> latIt = graph.find(s, 
SpatialExtension.GEO_LAT_NODE, Node.ANY);
+        ExtendedIterator<Entry<Node, GeometryWrapper>> result = 
WrappedIterator.create(Iter.iter(latIt).flatMap(triple -> {
+            Node feature = triple.getSubject();
+            Node lat = triple.getObject();
+
+            // Create the cross-product between lats and lons.
+            ExtendedIterator<Node> lons = G.iterSP(graph, feature, 
SpatialExtension.GEO_LON_NODE);
+
+            // On malformed data this can cause lots of log output. Perhaps 
it's better to keep validation separate from indexing.
+            int[] lonCounter = {0};
+            ExtendedIterator<Entry<Node, GeometryWrapper>> r = 
lons.mapWith(lon -> {
+                if (enableWarnings) {
+                    if (lonCounter[0] == 1) {
+                        if (!loggedMultipleLatLons[0]) {
+                            LOGGER.warn("Geo predicates: multiple longitudes 
detected on feature " + feature + ". Further warnings will be omitted.");
+                            loggedMultipleLatLons[0] = true;
+                        }
+                    }
+                    ++lonCounter[0];
+                }
+                GeometryWrapper geometryWrapper = 
ConvertLatLon.toGeometryWrapper(lat, lon);
+                return Map.entry(feature, geometryWrapper);
+            });
+            return r;
+        }));
+        return result;
+    }
+
+    /**
+     * Read lat/lon values for the given subject. Null if there are no such 
properties.
+     * Throws {@link DatatypeFormatException} when detecting incorrect use of 
these properties.
+     */
+    public static Node getGeoLiteral(Graph graph, Node s) {
+        Node lat = null;
+        try {
+            lat = G.getZeroOrOneSP(graph, s, SpatialExtension.GEO_LAT_NODE);
+        } catch (RDFDataException ex) {
+            throw new DatatypeFormatException(s + " has more than one geo:lat 
property.");
+        }
+
+        Node lon = null;
+        try {
+            lon = G.getZeroOrOneSP(graph, s, SpatialExtension.GEO_LON_NODE);
+        } catch ( RDFDataException ex) {
+            throw new DatatypeFormatException(s + " has more than one geo:lon 
property.");
+        }
+
+        // Both null -> return null.
+        if (lat == null && lon == null) {
+            return null;
+        }
+
+        if (lat == null) {
+            throw new DatatypeFormatException(s + " has a geo:lon property but 
is missing geo:lat.");
+        }
+        if (lon == null) {
+            throw new DatatypeFormatException(s + " has a geo:lat property but 
is missing geo:lon.");
+        }
+        Node geometryLiteral = ConvertLatLon.toNode(lat, lon);
+        return geometryLiteral;
+    }
+
+    private static boolean containsGeoLiteralProperties(Graph graph, Node s) {
+        boolean result =
+            graph.contains(s, SpatialExtension.GEO_LAT_NODE, null) ||
+            graph.contains(s, SpatialExtension.GEO_LON_NODE, null);
+        return result;
+    }
+}
diff --git 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/index/QueryRewriteIndex.java
 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/index/QueryRewriteIndex.java
index 6045174e65..c516da0ced 100644
--- 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/index/QueryRewriteIndex.java
+++ 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/index/QueryRewriteIndex.java
@@ -181,7 +181,7 @@ public class QueryRewriteIndex {
      */
     public static final void prepare(Dataset dataset) {
         Context context = dataset.getContext();
-        context.set(QUERY_REWRITE_INDEX_SYMBOL, createDefault());
+        set(context, createDefault());
     }
 
     /**
@@ -194,7 +194,7 @@ public class QueryRewriteIndex {
      */
     public static final void prepare(Dataset dataset, String 
queryRewriteLabel, int maxSize, long expiryInterval) {
         Context context = dataset.getContext();
-        context.set(QUERY_REWRITE_INDEX_SYMBOL, new 
QueryRewriteIndex(queryRewriteLabel, maxSize, expiryInterval));
+        set(context, new QueryRewriteIndex(queryRewriteLabel, maxSize, 
expiryInterval));
     }
 
     /**
@@ -204,10 +204,9 @@ public class QueryRewriteIndex {
      * @param execCxt
      * @return QueryRewriteIndex contained in the Context.
      */
-    public static final QueryRewriteIndex retrieve(ExecutionContext execCxt) {
-
+    public static final QueryRewriteIndex getOrCreate(ExecutionContext 
execCxt) {
         Context context = execCxt.getContext();
-        return retrieve(context);
+        return getOrCreate(context);
     }
 
     /**
@@ -217,10 +216,9 @@ public class QueryRewriteIndex {
      * @param dataset
      * @return QueryRewriteIndex contained in the Context.
      */
-    public static final QueryRewriteIndex retrieve(Dataset dataset) {
-
+    public static final QueryRewriteIndex getOrCreate(Dataset dataset) {
         Context context = dataset.getContext();
-        return retrieve(context);
+        return getOrCreate(context);
     }
 
     /**
@@ -230,15 +228,18 @@ public class QueryRewriteIndex {
      * @param context
      * @return QueryRewriteIndex contained in the Context.
      */
-    public static final QueryRewriteIndex retrieve(Context context) {
-        QueryRewriteIndex queryRewriteIndex = 
context.get(QUERY_REWRITE_INDEX_SYMBOL, null);
+    public static final QueryRewriteIndex getOrCreate(Context context) {
+        QueryRewriteIndex queryRewriteIndex = 
context.computeIfAbsent(QUERY_REWRITE_INDEX_SYMBOL, k -> createDefault());
+        return queryRewriteIndex;
+    }
 
-        if (queryRewriteIndex == null) {
-            queryRewriteIndex = createDefault();
-            context.set(QUERY_REWRITE_INDEX_SYMBOL, queryRewriteIndex);
-        }
+    public static final QueryRewriteIndex get(Context context) {
+        return (context == null) ? null : 
context.get(QUERY_REWRITE_INDEX_SYMBOL);
+    }
 
-        return queryRewriteIndex;
+    public static final Context set(Context context, QueryRewriteIndex 
queryRewriteIndex) {
+        context.set(QUERY_REWRITE_INDEX_SYMBOL, queryRewriteIndex);
+        return context;
     }
 
     /**
diff --git 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/SpatialIndexFindUtils.java
 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/SpatialIndexFindUtils.java
index cc9efa7e42..c2264faad4 100644
--- 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/SpatialIndexFindUtils.java
+++ 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/SpatialIndexFindUtils.java
@@ -17,29 +17,23 @@
  */
 package org.apache.jena.geosparql.spatial;
 
-import java.lang.invoke.MethodHandles;
 import java.util.Iterator;
 
 import org.apache.jena.atlas.iterator.Iter;
 import org.apache.jena.atlas.iterator.IteratorCloseable;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
-import org.apache.jena.geosparql.implementation.vocabulary.Geo;
-import org.apache.jena.geosparql.implementation.vocabulary.SpatialExtension;
+import org.apache.jena.geosparql.implementation.access.AccessGeoSPARQL;
+import org.apache.jena.geosparql.implementation.access.AccessWGS84;
 import org.apache.jena.graph.Graph;
 import org.apache.jena.graph.Node;
 import org.apache.jena.graph.Triple;
 import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.system.G;
 import org.locationtech.jts.geom.Envelope;
 import org.opengis.geometry.MismatchedDimensionException;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class SpatialIndexFindUtils {
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
     /**
      * Find Spatial Index Items from all graphs in Dataset.<br>
      *
@@ -47,22 +41,21 @@ public class SpatialIndexFindUtils {
      * @param srsURI
      * @return SpatialIndexItems found.
      */
-    public static IteratorCloseable<SpatialIndexItem> 
findSpatialIndexItems(DatasetGraph datasetGraph, String srsURI) {
+    public static IteratorCloseable<SpatialIndexItem> 
findIndexItems(DatasetGraph datasetGraph, String srsURI) {
         Graph defaultGraph = datasetGraph.getDefaultGraph();
-        IteratorCloseable<SpatialIndexItem> itemsIter = 
findSpatialIndexItems(defaultGraph, srsURI);
+        IteratorCloseable<SpatialIndexItem> itemsIter = 
findIndexItems(defaultGraph, srsURI);
         try {
             //Named Models
             Iterator<Node> graphNodeIt = datasetGraph.listGraphNodes();
             Iterator<SpatialIndexItem> namedGraphItemsIt = 
Iter.iter(graphNodeIt).flatMap(graphNode -> {
                 Graph namedGraph = datasetGraph.getGraph(graphNode);
-                IteratorCloseable<SpatialIndexItem> graphItems = 
findSpatialIndexItems(namedGraph, srsURI);
+                IteratorCloseable<SpatialIndexItem> graphItems = 
findIndexItems(namedGraph, srsURI);
                 return graphItems;
             });
             itemsIter = Iter.iter(itemsIter).append(namedGraphItemsIt);
         } catch(Throwable t) {
-            t.addSuppressed(new RuntimeException("Failure during 
findSpatialIndexItems.", t));
             Iter.close(itemsIter);
-            throw t;
+            throw new RuntimeException(t);
         }
         return itemsIter;
     }
@@ -74,69 +67,33 @@ public class SpatialIndexFindUtils {
      * @param srsURI
      * @return Items found in the Model in the SRS URI.
      */
-    public static final IteratorCloseable<SpatialIndexItem> 
findSpatialIndexItems(Graph graph, String srsURI) {
+    public static final IteratorCloseable<SpatialIndexItem> 
findIndexItems(Graph graph, String srsURI) {
         IteratorCloseable<SpatialIndexItem> result;
         // Only add one set of statements as a converted dataset will 
duplicate the same info.
-        if (graph.contains(null, Geo.HAS_GEOMETRY_NODE, null)) {
-            // LOGGER.info("Feature-hasGeometry-Geometry statements found.");
-            // if (graph.contains(null, SpatialExtension.GEO_LAT_NODE, null)) {
-            //     LOGGER.warn("Lat/Lon Geo predicates also found but will not 
be added to index.");
-            // }
-            result = findGeometryIndexItems(graph, srsURI);
-        } else if (graph.contains(null, SpatialExtension.GEO_LAT_NODE, null)) {
-            // LOGGER.info("Geo predicate statements found.");
-            result = findGeoPredicateIndexItems(graph, srsURI);
+        if (AccessGeoSPARQL.containsGeoLiterals(graph)) {
+            result = findIndexItemsGeoSparql(graph, srsURI);
+        } else if (AccessWGS84.containsGeoLiteralProperties(graph)) {
+            result = findIndexItemsWgs84(graph, srsURI);
         } else {
             result = Iter.empty();
         }
         return result;
     }
 
-    /** Print out log messages for what type of spatial data is found in the 
given graph. */
-    public static final void checkSpatialIndexItems(Graph graph) {
-        // Only add one set of statements as a converted dataset will 
duplicate the same info.
-        if (graph.contains(null, Geo.HAS_GEOMETRY_NODE, null)) {
-            LOGGER.info("Feature-hasGeometry-Geometry statements found.");
-            if (graph.contains(null, SpatialExtension.GEO_LAT_NODE, null)) {
-                LOGGER.warn("Lat/Lon Geo predicates also found but will not be 
added to index.");
-            }
-        } else if (graph.contains(null, SpatialExtension.GEO_LAT_NODE, null)) {
-            LOGGER.info("Geo predicate statements found.");
-        }
-    }
-
     /**
      *
      * @param graph
      * @param srsURI
      * @return SpatialIndexItem items prepared for adding to SpatialIndex.
      */
-    public static IteratorCloseable<SpatialIndexItem> 
findGeometryIndexItems(Graph graph, String srsURI) {
-        Iterator<Triple> stmtIter = graph.find(null, Geo.HAS_GEOMETRY_NODE, 
null);
+    public static IteratorCloseable<SpatialIndexItem> 
findIndexItemsGeoSparql(Graph graph, String srsURI) {
+        Iterator<Triple> stmtIter = 
AccessGeoSPARQL.findSpecificGeoResources(graph);
         IteratorCloseable<SpatialIndexItem> result = 
Iter.iter(stmtIter).flatMap(stmt -> {
             Node feature = stmt.getSubject();
             Node geometry = stmt.getObject();
-
-            Iterator<Node> nodeIter = G.iterSP(graph, geometry, 
Geo.HAS_SERIALIZATION_NODE);
-
-            // XXX If there is a super-property then the concrete 
serializations are not tried.
-            try {
-                if (!nodeIter.hasNext()) {
-                    Iter.close(nodeIter);
-
-                    Iterator<Node> wktNodeIter = G.iterSP(graph, geometry, 
Geo.AS_WKT_NODE);
-                    nodeIter = wktNodeIter;
-
-                    Iterator<Node> gmlNodeIter = G.iterSP(graph, geometry, 
Geo.AS_GML_NODE);
-                    nodeIter = Iter.append(wktNodeIter, gmlNodeIter);
-                }
-            } catch (Throwable t) {
-                t.addSuppressed(new RuntimeException("Error encountered.", t));
-                Iter.close(nodeIter);
-                throw t;
-            }
-
-            Iterator<SpatialIndexItem> itemIter = Iter.map(nodeIter, 
geometryNode -> {
+            Iterator<Triple> serializationIter = 
AccessGeoSPARQL.findSpecificGeoLiterals(graph, geometry);
+            Iterator<SpatialIndexItem> itemIter = Iter.map(serializationIter, 
triple -> {
+                Node geometryNode = triple.getObject();
                 GeometryWrapper geometryWrapper = 
GeometryWrapper.extract(geometryNode);
                 SpatialIndexItem item = makeSpatialIndexItem(feature, 
geometryWrapper, srsURI);
                 return item;
@@ -147,42 +104,19 @@ public class SpatialIndexFindUtils {
     }
 
     /**
+     *
      *
      * @param graph
      * @param srsURI
      * @return Geo predicate objects prepared for adding to SpatialIndex.
      */
-    public static IteratorCloseable<SpatialIndexItem> 
findGeoPredicateIndexItems(Graph graph, String srsURI) {
-        // Warn about multiple lat/lon combinations only at most once per 
graph.
-        boolean enableWarnings = false;
-        boolean[] loggedMultipleLatLons = { false };
-        Iterator<Triple> latIt = graph.find(Node.ANY, 
SpatialExtension.GEO_LAT_NODE, Node.ANY);
-        IteratorCloseable<SpatialIndexItem> result = 
Iter.iter(latIt).flatMap(triple -> {
-            Node feature = triple.getSubject();
-            Node lat = triple.getObject();
-
-            // Create the cross-product between lats and lons.
-            Iterator<Node> lons = G.iterSP(graph, feature, 
SpatialExtension.GEO_LON_NODE);
-
-            // On malformed data this can cause lots of log output. Perhaps 
it's better to keep validation separate from indexing.
-            int[] lonCounter = {0};
-            Iterator<SpatialIndexItem> r = Iter.iter(lons).map(lon -> {
-                if (enableWarnings) {
-                    if (lonCounter[0] == 1) {
-                        if (!loggedMultipleLatLons[0]) {
-                            LOGGER.warn("Geo predicates: multiple longitudes 
detected on feature " + feature + ". Further warnings will be omitted.");
-                            loggedMultipleLatLons[0] = true;
-                        }
-                    }
-                    ++lonCounter[0];
-                }
-                GeometryWrapper geometryWrapper = 
ConvertLatLon.toGeometryWrapper(lat, lon);
-                SpatialIndexItem item = makeSpatialIndexItem(feature, 
geometryWrapper, srsURI);
-                return item;
-            });
-            return r;
+    public static IteratorCloseable<SpatialIndexItem> 
findIndexItemsWgs84(Graph graph, String srsURI) {
+        return Iter.iter(AccessWGS84.findGeoLiterals(graph, null)).map(e -> {
+            Node feature = e.getKey();
+            GeometryWrapper geometryWrapper = e.getValue();
+            SpatialIndexItem item = makeSpatialIndexItem(feature, 
geometryWrapper, srsURI);
+            return item;
         });
-        return result;
     }
 
     public static SpatialIndexItem makeSpatialIndexItem(Node feature, 
GeometryWrapper geometryWrapper, String srsURI) {
diff --git 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/index/v2/STRtreeUtils.java
 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/index/v2/STRtreeUtils.java
index 37775eae53..15e40b2754 100644
--- 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/index/v2/STRtreeUtils.java
+++ 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/index/v2/STRtreeUtils.java
@@ -40,7 +40,7 @@ public class STRtreeUtils {
     public static STRtree buildSpatialIndexTree(Graph graph, String srsURI) 
throws SpatialIndexException {
         try {
             STRtree tree;
-            IteratorCloseable<SpatialIndexItem> it = 
SpatialIndexFindUtils.findSpatialIndexItems(graph, srsURI);
+            IteratorCloseable<SpatialIndexItem> it = 
SpatialIndexFindUtils.findIndexItems(graph, srsURI);
             try {
                 tree = buildSpatialIndexTree(it);
             } finally {
diff --git 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/index/v2/SpatialIndexLib.java
 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/index/v2/SpatialIndexLib.java
index 4395de06b4..7541912e9d 100644
--- 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/index/v2/SpatialIndexLib.java
+++ 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/index/v2/SpatialIndexLib.java
@@ -112,13 +112,13 @@ public class SpatialIndexLib {
     }
 
     /**
-     * Retrieve the SpatialIndex from the Context.
+     * Get the SpatialIndex from the Context. Fail if absent.
      *
      * @param execCxt
      * @return SpatialIndex contained in the Context.
      * @throws SpatialIndexException
      */
-    public static final SpatialIndex retrieve(ExecutionContext execCxt) throws 
SpatialIndexException {
+    public static final SpatialIndex require(ExecutionContext execCxt) throws 
SpatialIndexException {
         Context context = execCxt.getContext();
         SpatialIndex spatialIndex = (SpatialIndex) 
context.get(SpatialIndexConstants.symSpatialIndex, null);
         if (spatialIndex == null) {
diff --git 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/property_functions/GenericSpatialPropertyFunction.java
 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/property_functions/GenericSpatialPropertyFunction.java
index 12625b59ac..a932ffc43c 100644
--- 
a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/property_functions/GenericSpatialPropertyFunction.java
+++ 
b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/property_functions/GenericSpatialPropertyFunction.java
@@ -17,19 +17,16 @@
  */
 package org.apache.jena.geosparql.spatial.property_functions;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.List;
 import java.util.stream.Stream;
 
-import org.apache.commons.collections4.iterators.IteratorChain;
+import org.apache.jena.atlas.iterator.Iter;
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
 import org.apache.jena.geosparql.implementation.SRSInfo;
-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.geosparql.implementation.access.AccessGeoSPARQL;
+import org.apache.jena.geosparql.implementation.access.AccessWGS84;
 import org.apache.jena.geosparql.spatial.SearchEnvelope;
 import org.apache.jena.geosparql.spatial.SpatialIndex;
 import org.apache.jena.geosparql.spatial.SpatialIndexException;
@@ -49,8 +46,6 @@ import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.pfunction.PFuncSimpleAndList;
 import org.apache.jena.sparql.pfunction.PropFuncArg;
 import org.apache.jena.sparql.util.FmtUtils;
-import org.apache.jena.system.G;
-import org.apache.jena.util.iterator.ExtendedIterator;
 
 /**
  *
@@ -66,7 +61,7 @@ public abstract class GenericSpatialPropertyFunction extends 
PFuncSimpleAndList
     @Override
     public final QueryIterator execEvaluated(Binding binding, Node subject, 
Node predicate, PropFuncArg object, ExecutionContext execCxt) {
         try {
-            spatialIndex = SpatialIndexLib.retrieve(execCxt);
+            spatialIndex = SpatialIndexLib.require(execCxt);
             spatialArguments = extractObjectArguments(predicate, object, 
spatialIndex.getSrsInfo());
             return search(binding, execCxt, subject, spatialArguments.limit);
         } catch (SpatialIndexException ex) {
@@ -110,47 +105,23 @@ public abstract class GenericSpatialPropertyFunction 
extends PFuncSimpleAndList
         try {
             Graph graph = execCxt.getActiveGraph();
 
-            IteratorChain<Triple> spatialTriples = new IteratorChain<>();
-
-            //Check for Geometry and so GeometryLiterals.
-            if (graph.contains(subject, Geo.HAS_GEOMETRY_NODE, null)) {
-                //A Feature can have many geometries so add each of them. The 
check Geo.HAS_DEFAULT_GEOMETRY_NODE will only return one but requires the data 
to have these present.
-                List<Node> geometryNodes = G.listSP(graph, subject, 
Geo.HAS_GEOMETRY_NODE);
-                geometryNodes.forEach(geometry->{
-                    ExtendedIterator<Triple> iter = graph.find(geometry, 
Geo.HAS_SERIALIZATION_NODE, null);
-                    // Check for asWKT
-                    if (!iter.hasNext()) {
-                        iter = graph.find(geometry, Geo.AS_WKT_NODE, null);
-                    }
-                    // Check for asGML
-                    if (!iter.hasNext()) {
-                        iter = graph.find(geometry, Geo.AS_GML_NODE, null);
-                    }
-                    spatialTriples.addIterator(iter);
-                });
+            Iterator<Triple> spatialTriples;
+
+            if (AccessGeoSPARQL.containsGeoLiterals(graph)) {
+                spatialTriples = 
AccessGeoSPARQL.findSpecificGeoLiteralsByFeature(graph, subject);
+            } else if (AccessWGS84.containsGeoLiteralProperties(graph)) {
+                spatialTriples = AccessWGS84.findGeoLiteralsAsTriples(graph, 
subject);
             } else {
-                //Check for Geo predicates against the feature when no 
geometry literals found.
-                if (graph.contains(subject, SpatialExtension.GEO_LAT_NODE, 
null) && graph.contains(subject, SpatialExtension.GEO_LON_NODE, null)) {
-                    Node lat = G.getOneSP(graph, subject, 
SpatialExtension.GEO_LAT_NODE);
-                    Node lon = G.getOneSP(graph, subject, 
SpatialExtension.GEO_LON_NODE);
-                    Node latLonGeometryLiteral = ConvertLatLon.toNode(lat, 
lon);
-                    Triple triple = Triple.create(subject, 
Geo.HAS_GEOMETRY_NODE, latLonGeometryLiteral);
-                    
spatialTriples.addIterator(Arrays.asList(triple).iterator());
-                }
+                spatialTriples = Iter.empty();
             }
 
             //Check through each Geometry and stop if one is accepted.
-            boolean isMatched = false;
-            while (spatialTriples.hasNext()) {
-                Triple triple = spatialTriples.next();
+            boolean isMatched = Iter.anyMatch(spatialTriples, triple -> {
                 Node geometryLiteral = triple.getObject();
                 GeometryWrapper targetGeometryWrapper = 
GeometryWrapper.extract(geometryLiteral);
-                isMatched = checkSecondFilter(spatialArguments, 
targetGeometryWrapper);
-                if (isMatched) {
-                    //Stop checking when match is true.
-                    break;
-                }
-            }
+                boolean isMatch = checkSecondFilter(spatialArguments, 
targetGeometryWrapper);
+                return isMatch;
+            });
 
             return isMatched;
         } catch (DatatypeFormatException ex) {
diff --git 
a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geo/topological/property_functions/simple_features/SfPFMiscSparqlTest.java
 
b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geo/topological/property_functions/simple_features/SfPFMiscSparqlTest.java
new file mode 100644
index 0000000000..a329e1cb32
--- /dev/null
+++ 
b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geo/topological/property_functions/simple_features/SfPFMiscSparqlTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.property_functions.simple_features;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFParser;
+import org.apache.jena.sparql.algebra.Table;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.exec.QueryExec;
+import org.apache.jena.sparql.sse.SSE;
+import org.junit.Test;
+
+/** Miscellaneous SPARQL-based tests across the simple feature family of 
property functions. */
+public class SfPFMiscSparqlTest {
+
+    @Test
+    public void test01() {
+        String query = """
+            PREFIX geo: <http://www.opengis.net/ont/geosparql#>
+            PREFIX ogcsf: <http://www.opengis.net/ont/sf#>
+            SELECT * {
+              ?s a ogcsf:Point .
+              ?s geo:sfWithin <urn:test:geosparql#geoFrance> .
+            } ORDER BY ?s
+        """;
+
+        DatasetGraph dsg = createTestDataFrance();
+        Table actual = QueryExec.dataset(dsg).query(query).table();
+        Table expected = SSE.parseTable("(table (row (?s 
<urn:test:geosparql#geoStrasbourg>) ))");
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void test02() {
+        String query = """
+            PREFIX geo: <http://www.opengis.net/ont/geosparql#>
+            SELECT * {
+              ?s geo:sfWithin <urn:test:geosparql#geoFrance> .
+            } ORDER BY ?s
+        """;
+
+        // Note: sfWithin is reflexive so 'geoFrance' is really expected as a 
result.
+        DatasetGraph dsg = createTestDataFrance();
+        Table actual = QueryExec.dataset(dsg).query(query).table();
+        Table expected = SSE.parseTable("(table (row (?s 
<urn:test:geosparql#geoFrance>) ) (row (?s <urn:test:geosparql#geoStrasbourg>) 
))");
+        assertEquals(expected, actual);
+    }
+
+    // Test data derived from GH-3473.
+    private static DatasetGraph createTestDataFrance() {
+        String data = """
+            PREFIX : <urn:test:geosparql#>
+            PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+            PREFIX geo: <http://www.opengis.net/ont/geosparql#>
+            PREFIX ogcsf: <http://www.opengis.net/ont/sf#>
+
+            :France
+                rdfs:label "France";
+                geo:hasGeometry :geoFrance.
+
+            :geoFrance a ogcsf:Polygon ;
+                # This is a bounding box of France.
+                geo:asWKT "POLYGON((-4.9423 41.3247, -4.9423 51.1496, 10.02105 
51.1496, 10.0210 41.3247, -4.9423 41.3247))"^^geo:wktLiteral .
+
+            :Strasbourg
+                rdfs:label "Strasbourg";
+                geo:hasGeometry :geoStrasbourg.
+
+            :geoStrasbourg a ogcsf:Point ;
+                geo:asWKT "POINT(7.7510 48.5819)"^^geo:wktLiteral .
+
+            # This point is outside of France's BBOX.
+            :Berlin
+                rdfs:label "Berlin";
+                geo:hasGeometry :geoBerlin.
+
+            :geoBerlin a ogcsf:Point ;
+                geo:asWKT "POINT (13.4050 52.5200)"^^geo:wktLiteral .
+        """;
+        return 
RDFParser.create().fromString(data).lang(Lang.TRIG).toDatasetGraph();
+    }
+}

Reply via email to