You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2022/03/26 10:35:29 UTC

[jena] branch main updated: Geosparql transformation options (#1225)

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 a2d29f7  Geosparql transformation options (#1225)
a2d29f7 is described below

commit a2d29f7eb3f14f1edd2ddae13a8813e5d83dd14f
Author: GregAlbo <46...@users.noreply.github.com>
AuthorDate: Sat Mar 26 10:35:23 2022 +0000

    Geosparql transformation options (#1225)
    
    * Options to control the dynamic transformation of Geometry Literals.
    * Added one-time log warning to GeometryWrapper.buffer(...) about additional error from units conversion.
    * GeoSPARQLConfig allows Geometry SRS transformation by default.
    * Tests updated for exceptions thrown when unit transformation disabled.
    * Conversion between linear and non-linear now throws exception due to risk of undisclosed error, inconsistency and confusion.
    * transformGeometry option added to allow disabling transformation of mismatching geometry literals.
    * Spatial Index now written to temp file before copying to target location.
    * Tests added for GeometryWrapper:getUTMZoneURI
---
 .../jena/fuseki/geosparql/DatasetOperations.java   |    6 +-
 .../org/apache/jena/fuseki/geosparql/Main.java     |   12 +-
 .../jena/fuseki/geosparql/cli/ArgsConfig.java      |   12 +-
 .../geosparql/configuration/GeoSPARQLConfig.java   |   16 +-
 .../filter_functions/BoundaryFF.java               |    3 +-
 .../nontopological/filter_functions/BufferFF.java  |    3 +-
 .../filter_functions/ConvexHullFF.java             |    3 +-
 .../filter_functions/DifferenceFF.java             |    3 +-
 .../filter_functions/DistanceFF.java               |    3 +-
 .../nontopological/filter_functions/EnvelopFF.java |    3 +-
 .../filter_functions/IntersectionFF.java           |    3 +-
 .../filter_functions/SymmetricDifferenceFF.java    |    3 +-
 .../nontopological/filter_functions/UnionFF.java   |    3 +-
 .../geosparql/implementation/GeometryWrapper.java  | 2488 ++++++++++----------
 .../geosparql/implementation/UnitsOfMeasure.java   |   26 +-
 .../jena/geosparql/spatial/SpatialIndex.java       |   18 +-
 .../spatial/filter_functions/DistanceFF.java       |    3 +-
 .../spatial/filter_functions/GreatCircleFF.java    |   10 +-
 .../filter_functions/GreatCircleGeomFF.java        |    3 +-
 .../spatial/filter_functions/NearbyFF.java         |    5 +-
 .../filter_functions/BufferFFTest.java             |   74 +-
 .../filter_functions/DistanceFFTest.java           |   75 +-
 .../implementation/GeometryWrapperTest.java        |   58 +-
 .../filter_functions/GreatCircleFFTest.java        |   27 +-
 .../filter_functions/GreatCircleGeomFFTest.java    |   13 +
 25 files changed, 1469 insertions(+), 1404 deletions(-)

diff --git a/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/DatasetOperations.java b/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/DatasetOperations.java
index d6b5eff..f41c454 100644
--- a/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/DatasetOperations.java
+++ b/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/DatasetOperations.java
@@ -94,6 +94,8 @@ public class DatasetOperations {
             GeoSPARQLConfig.setupNoIndex(argsConfig.isQueryRewrite());
         }
 
+        GeoSPARQLConfig.allowGeometrySRSTransformation(argsConfig.isTransformGeometry());
+
         //Setup Spatial Extension
         prepareSpatialExtension(dataset, argsConfig);
 
@@ -106,9 +108,9 @@ public class DatasetOperations {
         File tdbFolder = argsConfig.getTdbFile();
         if (tdbFolder != null) {
             LOGGER.info("TDB Dataset: {}, TDB2: {}", tdbFolder, argsConfig.isTDB2());
-            if(argsConfig.isTDB2()){
+            if (argsConfig.isTDB2()) {
                 dataset = TDB2Factory.connectDataset(tdbFolder.getAbsolutePath());
-            }else{
+            } else {
                 dataset = TDBFactory.createDataset(tdbFolder.getAbsolutePath());
             }
         } else {
diff --git a/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/Main.java b/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/Main.java
index 997142a..387dbec 100644
--- a/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/Main.java
+++ b/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/Main.java
@@ -20,17 +20,20 @@ package org.apache.jena.fuseki.geosparql;
 import com.beust.jcommander.JCommander;
 import java.lang.invoke.MethodHandles;
 import java.util.Arrays;
-import org.apache.jena.geosparql.configuration.SrsException;
-import org.apache.jena.geosparql.spatial.SpatialIndexException;
 import org.apache.jena.fuseki.geosparql.cli.ArgsConfig;
 import org.apache.jena.fuseki.system.FusekiLogging;
+import org.apache.jena.geosparql.configuration.SrsException;
+import org.apache.jena.geosparql.spatial.SpatialIndexException;
 import org.apache.jena.query.Dataset;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.bridge.SLF4JBridgeHandler;
 
 public class Main {
-    static { FusekiLogging.setLogging(); }
+
+    static {
+        FusekiLogging.setLogging();
+    }
 
     private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
@@ -42,8 +45,7 @@ public class Main {
         //Apache SIS j.u.l logging redirection.
         SLF4JBridgeHandler.removeHandlersForRootLogger();
         SLF4JBridgeHandler.install();
-        
-        
+
         LOGGER.info("Arguments Received: {}", Arrays.asList(args));
 
         ArgsConfig argsConfig = new ArgsConfig();
diff --git a/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/cli/ArgsConfig.java b/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/cli/ArgsConfig.java
index aa812b8..6f47947 100644
--- a/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/cli/ArgsConfig.java
+++ b/jena-fuseki2/jena-fuseki-geosparql/src/main/java/org/apache/jena/fuseki/geosparql/cli/ArgsConfig.java
@@ -103,7 +103,11 @@ public class ArgsConfig {
     @Parameter(names = {"--tdb2", "-t2"}, description = "Option to use TDB2, rather than TDB, for persistent storage. Default: false", order = 17)
     private boolean tdb2 = false;
 
-    //19) Help
+    //19) Geometry Transformation
+    @Parameter(names = {"--transform_geometry", "-tg"}, description = "Option to disable transformation of mismatching Geometry SRS. Default: true", arity = 1, order = 18)
+    private boolean transformGeometry = true;
+
+    //20) Help
     @Parameter(names = {"--help", "-h"}, description = "Application help. @path/to/file can be used to submit parameters in a file.", help = true, order = 18)
     private boolean help = false;
 
@@ -195,12 +199,16 @@ public class ArgsConfig {
         return tdb2;
     }
 
+    public boolean isTransformGeometry() {
+        return transformGeometry;
+    }
+
     public boolean isHelp() {
         return help;
     }
 
     public String getSummary() {
-        return "port=" + port + ", datsetName=" + datsetName + ", loopbackOnly=" + loopbackOnly + ", updateAllowed=" + updateAllowed + ", inference=" + inference + ", applyDefaultGeometry=" + applyDefaultGeometry + ", validateGeometryLiteral=" + validateGeometryLiteral + ", convertGeoPredicates=" + convertGeoPredicates + ", removeGeoPredicates=" + removeGeoPredicates + ", queryRewrite=" + queryRewrite + ", tdbFile=" + tdbFile + ", fileGraphFormats=" + fileGraphFormats + ", fileGraphDelim [...]
+        return "port=" + port + ", datsetName=" + datsetName + ", loopbackOnly=" + loopbackOnly + ", updateAllowed=" + updateAllowed + ", inference=" + inference + ", applyDefaultGeometry=" + applyDefaultGeometry + ", validateGeometryLiteral=" + validateGeometryLiteral + ", convertGeoPredicates=" + convertGeoPredicates + ", removeGeoPredicates=" + removeGeoPredicates + ", queryRewrite=" + queryRewrite + ", tdbFile=" + tdbFile + ", fileGraphFormats=" + fileGraphFormats + ", fileGraphDelim [...]
     }
 
     @Override
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/configuration/GeoSPARQLConfig.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/configuration/GeoSPARQLConfig.java
index b08eb31..c6ca10f 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/configuration/GeoSPARQLConfig.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/configuration/GeoSPARQLConfig.java
@@ -18,7 +18,6 @@
 package org.apache.jena.geosparql.configuration;
 
 import java.io.File;
-
 import org.apache.jena.geosparql.geof.topological.RelateFF;
 import org.apache.jena.geosparql.implementation.datatype.GeometryDatatype;
 import org.apache.jena.geosparql.implementation.function_registration.*;
@@ -58,6 +57,11 @@ public class GeoSPARQLConfig {
     public static int PRECISION_MODEL_SCALE_FACTOR = 1000000;
 
     /**
+     * Option to dynamically transform GeometryLiteral SRS in calculations.
+     */
+    public static boolean ALLOW_GEOMETRY_SRS_TRANSFORMATION = true;
+
+    /**
      * Initialise all GeoSPARQL property and filter functions with memory
      * indexing.
      * <br>Use this for in-memory indexing GeoSPARQL setup. Query re-write
@@ -197,7 +201,6 @@ public class GeoSPARQLConfig {
              *   Dec 28, 2021 10:56:27 AM org.apache.sis.referencing.factory.sql.EPSGFactory <init>
              *   WARNING: The “SIS_DATA” environment variable is not set.
              */
-
             //Setup Default Coordinate Reference Systems
             SRSRegistry.setupDefaultSRS();
 
@@ -331,4 +334,13 @@ public class GeoSPARQLConfig {
         PRECISION_MODEL_SCALE_FACTOR = scaleFactor;
     }
 
+    /**
+     * Sets whether transformation for mismatching Geometry SRS is allowed.
+     *
+     * @param allowTransformation
+     */
+    public static final void allowGeometrySRSTransformation(boolean allowTransformation) {
+        ALLOW_GEOMETRY_SRS_TRANSFORMATION = allowTransformation;
+    }
+
 }
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BoundaryFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BoundaryFF.java
index f269952..d95b94b 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BoundaryFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BoundaryFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.geof.nontopological.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
 import org.apache.jena.sparql.function.FunctionBase1;
@@ -38,7 +39,7 @@ public class BoundaryFF extends FunctionBase1 {
 
             GeometryWrapper boundary = geometry.boundary();
             return boundary.asNodeValue();
-        } catch (DatatypeFormatException ex) {
+        } catch (DatatypeFormatException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
         }
     }
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BufferFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BufferFF.java
index f9e73d4..9835471 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BufferFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BufferFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.geof.nontopological.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.graph.Node;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
@@ -61,7 +62,7 @@ public class BufferFF extends FunctionBase3 {
             return buffer.asNodeValue();
         } catch (DatatypeFormatException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
-        } catch (FactoryException | MismatchedDimensionException | TransformException ex) {
+        } catch (FactoryException | MismatchedDimensionException | TransformException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage() + ": " + FmtUtils.stringForNode(v1.asNode()) + ", " + FmtUtils.stringForNode(v2.asNode()) + ", " + FmtUtils.stringForNode(v3.asNode()), ex);
         }
 
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/ConvexHullFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/ConvexHullFF.java
index f6850cd..8d0dc24 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/ConvexHullFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/ConvexHullFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.geof.nontopological.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
 import org.apache.jena.sparql.function.FunctionBase1;
@@ -38,7 +39,7 @@ public class ConvexHullFF extends FunctionBase1 {
 
             GeometryWrapper convexHull = geometry.convexHull();
             return convexHull.asNodeValue();
-        } catch (DatatypeFormatException ex) {
+        } catch (DatatypeFormatException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
         }
     }
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DifferenceFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DifferenceFF.java
index ca27288..7323dda 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DifferenceFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DifferenceFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.geof.nontopological.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.geosparql.implementation.index.GeometryLiteralIndex.GeometryIndex;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
@@ -47,7 +48,7 @@ public class DifferenceFF extends FunctionBase2 {
 
         } catch (DatatypeFormatException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
-        } catch (FactoryException | MismatchedDimensionException | TransformException ex) {
+        } catch (FactoryException | MismatchedDimensionException | TransformException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage() + ": " + FmtUtils.stringForNode(v1.asNode()) + ", " + FmtUtils.stringForNode(v2.asNode()), ex);
         }
 
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DistanceFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DistanceFF.java
index 6e5ffd9..66504df 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DistanceFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DistanceFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.geof.nontopological.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.geosparql.implementation.index.GeometryLiteralIndex.GeometryIndex;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
@@ -52,7 +53,7 @@ public class DistanceFF extends FunctionBase3 {
             return NodeValue.makeDouble(distance);
         } catch (DatatypeFormatException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
-        } catch (FactoryException | MismatchedDimensionException | TransformException ex) {
+        } catch (FactoryException | MismatchedDimensionException | TransformException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage() + ": " + FmtUtils.stringForNode(v1.asNode()) + ", " + FmtUtils.stringForNode(v2.asNode()) + ", " + FmtUtils.stringForNode(v3.asNode()), ex);
         }
 
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/EnvelopFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/EnvelopFF.java
index 2127d69..c3abc2f 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/EnvelopFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/EnvelopFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.geof.nontopological.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
 import org.apache.jena.sparql.function.FunctionBase1;
@@ -38,7 +39,7 @@ public class EnvelopFF extends FunctionBase1 {
 
             GeometryWrapper envelope = geometry.envelope();
             return envelope.asNodeValue();
-        } catch (DatatypeFormatException ex) {
+        } catch (DatatypeFormatException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
         }
     }
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/IntersectionFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/IntersectionFF.java
index 2651661..3e6fb41 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/IntersectionFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/IntersectionFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.geof.nontopological.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.geosparql.implementation.index.GeometryLiteralIndex.GeometryIndex;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
@@ -48,7 +49,7 @@ public class IntersectionFF extends FunctionBase2 {
 
         } catch (DatatypeFormatException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
-        } catch (FactoryException | MismatchedDimensionException | TransformException ex) {
+        } catch (FactoryException | MismatchedDimensionException | TransformException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage() + ": " + FmtUtils.stringForNode(v1.asNode()) + ", " + FmtUtils.stringForNode(v2.asNode()), ex);
         }
 
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/SymmetricDifferenceFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/SymmetricDifferenceFF.java
index a87b62f..8fb1c65 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/SymmetricDifferenceFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/SymmetricDifferenceFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.geof.nontopological.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.geosparql.implementation.index.GeometryLiteralIndex.GeometryIndex;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
@@ -47,7 +48,7 @@ public class SymmetricDifferenceFF extends FunctionBase2 {
 
         } catch (DatatypeFormatException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
-        } catch (FactoryException | MismatchedDimensionException | TransformException ex) {
+        } catch (FactoryException | MismatchedDimensionException | TransformException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage() + ": " + FmtUtils.stringForNode(v1.asNode()) + ", " + FmtUtils.stringForNode(v2.asNode()), ex);
         }
 
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/UnionFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/UnionFF.java
index f725388..385b64d 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/UnionFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/UnionFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.geof.nontopological.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.geosparql.implementation.index.GeometryLiteralIndex.GeometryIndex;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
@@ -47,7 +48,7 @@ public class UnionFF extends FunctionBase2 {
 
         } catch (DatatypeFormatException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
-        } catch (FactoryException | MismatchedDimensionException | TransformException ex) {
+        } catch (FactoryException | MismatchedDimensionException | TransformException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage() + ": " + FmtUtils.stringForNode(v1.asNode()) + ", " + FmtUtils.stringForNode(v2.asNode()), ex);
         }
 
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/GeometryWrapper.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/GeometryWrapper.java
index 4de0749..d93109f 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/GeometryWrapper.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/GeometryWrapper.java
@@ -1,1248 +1,1240 @@
-/*
- * 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;
-
-import java.io.Serializable;
-import java.util.Objects;
-import org.apache.jena.datatypes.DatatypeFormatException;
-import org.apache.jena.geosparql.implementation.datatype.GMLDatatype;
-import org.apache.jena.geosparql.implementation.datatype.GeometryDatatype;
-import org.apache.jena.geosparql.implementation.datatype.WKTDatatype;
-import org.apache.jena.geosparql.implementation.great_circle.CoordinatePair;
-import org.apache.jena.geosparql.implementation.great_circle.GreatCircleDistance;
-import org.apache.jena.geosparql.implementation.index.GeometryLiteralIndex.GeometryIndex;
-import org.apache.jena.geosparql.implementation.index.GeometryTransformIndex;
-import org.apache.jena.geosparql.implementation.jts.CoordinateSequenceDimensions;
-import org.apache.jena.geosparql.implementation.jts.CustomCoordinateSequence;
-import org.apache.jena.geosparql.implementation.jts.CustomGeometryFactory;
-import org.apache.jena.geosparql.implementation.registry.MathTransformRegistry;
-import org.apache.jena.geosparql.implementation.registry.SRSRegistry;
-import org.apache.jena.geosparql.implementation.registry.UnitsRegistry;
-import org.apache.jena.geosparql.implementation.vocabulary.SRS_URI;
-import org.apache.jena.geosparql.implementation.vocabulary.Unit_URI;
-import org.apache.jena.graph.Node;
-import org.apache.jena.rdf.model.Literal;
-import org.apache.jena.rdf.model.ResourceFactory;
-import org.apache.jena.sparql.expr.NodeValue;
-import org.apache.sis.geometry.DirectPosition2D;
-import org.locationtech.jts.geom.Coordinate;
-import org.locationtech.jts.geom.Envelope;
-import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.GeometryFactory;
-import org.locationtech.jts.geom.IntersectionMatrix;
-import org.locationtech.jts.geom.Point;
-import org.locationtech.jts.geom.prep.PreparedGeometry;
-import org.locationtech.jts.geom.prep.PreparedGeometryFactory;
-import org.locationtech.jts.geom.util.AffineTransformation;
-import org.opengis.geometry.DirectPosition;
-import org.opengis.geometry.MismatchedDimensionException;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.TransformException;
-import org.opengis.util.FactoryException;
-
-/**
- *
- *
- */
-public class GeometryWrapper implements Serializable {
-
-    private final DimensionInfo dimensionInfo;
-    private final SRSInfo srsInfo;
-    private final Geometry xyGeometry;
-    private final Geometry parsingGeometry;
-    private PreparedGeometry preparedGeometry;
-    private Envelope envelope;
-    private Geometry translateXYGeometry;
-    private final String geometryDatatypeURI;
-    private GeometryDatatype geometryDatatype;
-    private String lexicalForm;
-    private String utmURI = null;
-    private Double latitude = null;
-
-    /**
-     *
-     * @param geometry In X/Y or Y/X coordinate order of the SRS URI.
-     * @param srsURI
-     * @param geometryDatatypeURI
-     * @param dimensionInfo
-     */
-    public GeometryWrapper(Geometry geometry, String srsURI, String geometryDatatypeURI, DimensionInfo dimensionInfo) {
-        this(geometry, srsURI, geometryDatatypeURI, dimensionInfo, null);
-    }
-
-    /**
-     *
-     * @param geometry In X/Y or Y/X coordinate order of the SRS URI.
-     * @param srsURI
-     * @param geometryDatatypeURI
-     * @param dimensionInfo
-     * @param geometryLiteral
-     */
-    public GeometryWrapper(Geometry geometry, String srsURI, String geometryDatatypeURI, DimensionInfo dimensionInfo, String geometryLiteral) {
-        this(geometry, GeometryReverse.check(geometry, srsURI.isEmpty() ? SRS_URI.DEFAULT_WKT_CRS84 : srsURI), srsURI.isEmpty() ? SRS_URI.DEFAULT_WKT_CRS84 : srsURI, geometryDatatypeURI, dimensionInfo, geometryLiteral);
-    }
-
-    protected GeometryWrapper(Geometry parsingGeometry, Geometry xyGeometry, String srsURI, String geometryDatatypeURI, DimensionInfo dimensionInfo) {
-        this(parsingGeometry, xyGeometry, srsURI, geometryDatatypeURI, dimensionInfo, null);
-    }
-
-    protected GeometryWrapper(Geometry parsingGeometry, Geometry xyGeometry, String srsURI, String geometryDatatypeURI, DimensionInfo dimensionInfo, String lexicalForm) {
-
-        this.parsingGeometry = parsingGeometry;
-        this.xyGeometry = xyGeometry;
-        this.preparedGeometry = null; //Initialised when required by spatial relations checkPreparedGeometry.
-        this.envelope = null; //Initialised when required by getEnvelope().
-        this.translateXYGeometry = null; //Initialised when required by translateGeometry().
-        this.geometryDatatypeURI = geometryDatatypeURI;
-        this.geometryDatatype = null; //Inilialised when required by getGeometryDatatype().
-
-        if (srsURI.isEmpty()) {
-            srsURI = SRS_URI.DEFAULT_WKT_CRS84;
-        }
-
-        this.srsInfo = SRSRegistry.getSRSInfo(srsURI);
-
-        this.dimensionInfo = dimensionInfo;
-        this.lexicalForm = lexicalForm; //If not Initialised then required by asLiteral() etc.
-    }
-
-    /**
-     * Default to WGS84 geometry and XY coordinate dimensions.
-     *
-     * @param geometry In X/Y or Y/X coordinate order of WGS84.
-     * @param geometryDatatypeURI
-     */
-    public GeometryWrapper(Geometry geometry, String geometryDatatypeURI) {
-        this(geometry, "", geometryDatatypeURI, DimensionInfo.XY_POINT);
-    }
-
-    /**
-     * Default to XY coordinate dimensions.
-     *
-     * @param geometry In X/Y or Y/X coordinate order of the SRS URI.
-     * @param srsURI
-     * @param geometryDatatypeURI
-     */
-    public GeometryWrapper(Geometry geometry, String srsURI, String geometryDatatypeURI) {
-        this(geometry, srsURI, geometryDatatypeURI, DimensionInfo.XY_POINT);
-    }
-
-    /**
-     * Empty geometry with specified parameters.
-     *
-     * @param srsURI
-     * @param geometryDatatypeURI
-     */
-    public GeometryWrapper(String srsURI, String geometryDatatypeURI) {
-        this(new CustomCoordinateSequence(DimensionInfo.XY_POINT.getDimensions()), geometryDatatypeURI, srsURI);
-    }
-
-    /**
-     * Point geometry with specified SRS.
-     *
-     * @param pointCoordinateSequence
-     * @param geometryDatatypeURI
-     * @param srsURI
-     */
-    public GeometryWrapper(CustomCoordinateSequence pointCoordinateSequence, String geometryDatatypeURI, String srsURI) {
-        this(CustomGeometryFactory.theInstance().createPoint(pointCoordinateSequence), srsURI, geometryDatatypeURI, DimensionInfo.XY_POINT);
-    }
-
-    /**
-     * Copy GeometryWrapper.
-     *
-     * @param geometryWrapper
-     */
-    public GeometryWrapper(GeometryWrapper geometryWrapper) {
-
-        this.xyGeometry = geometryWrapper.xyGeometry;
-        this.parsingGeometry = geometryWrapper.parsingGeometry;
-        this.preparedGeometry = geometryWrapper.preparedGeometry;
-        this.envelope = geometryWrapper.envelope;
-        this.translateXYGeometry = geometryWrapper.translateXYGeometry;
-        this.utmURI = geometryWrapper.utmURI;
-        this.latitude = geometryWrapper.latitude;
-        this.geometryDatatypeURI = geometryWrapper.geometryDatatypeURI;
-        this.geometryDatatype = geometryWrapper.geometryDatatype;
-
-        this.srsInfo = geometryWrapper.srsInfo;
-        this.dimensionInfo = geometryWrapper.dimensionInfo;
-        this.lexicalForm = geometryWrapper.lexicalForm;
-    }
-
-    /**
-     * Transforms, if necessary, the provided target GeometryWrapper according
-     * to this GeometryWrapper SRS_URI.
-     *
-     * @param targetGeometryWrapper
-     * @return GeometryWrapper after transformation.
-     * @throws FactoryException
-     * @throws MismatchedDimensionException
-     * @throws TransformException
-     */
-    public GeometryWrapper checkTransformSRS(GeometryWrapper targetGeometryWrapper) throws FactoryException, MismatchedDimensionException, TransformException {
-
-        GeometryWrapper transformedGeometryWrapper;
-        String srsURI = srsInfo.getSrsURI();
-        if (srsURI.equals(targetGeometryWrapper.srsInfo.getSrsURI())) {
-            transformedGeometryWrapper = targetGeometryWrapper;
-        } else {
-            transformedGeometryWrapper = targetGeometryWrapper.transform(srsURI);
-        }
-
-        return transformedGeometryWrapper;
-    }
-
-    /**
-     * Transform the GeometryWrapper into another spatial reference system.<br>
-     *
-     * @param srsURI
-     * @return New GeometryWrapper after transformation, or this GeometryWrapper
-     * if no transformation.
-     * @throws MismatchedDimensionException
-     * @throws TransformException
-     * @throws FactoryException
-     */
-    public GeometryWrapper transform(String srsURI) throws MismatchedDimensionException, TransformException, FactoryException {
-        return transform(srsURI, true);
-    }
-
-    /**
-     * Transform the GeometryWrapper into another spatial reference system.<br>
-     *
-     * @param srsInfo
-     * @return New GeometryWrapper after transformation, or this GeometryWrapper
-     * if no transformation.
-     * @throws MismatchedDimensionException
-     * @throws TransformException
-     * @throws FactoryException
-     */
-    public GeometryWrapper transform(SRSInfo srsInfo) throws MismatchedDimensionException, TransformException, FactoryException {
-        return transform(srsInfo.getSrsURI(), true);
-    }
-
-    /**
-     * Transform the GeometryWrapper into another spatial reference system.<br>
-     * Option to store the resulting GeometryWrapper in the index.
-     *
-     * @param srsURI
-     * @param storeSRSTransform
-     * @return GeometryWrapper after transformation.
-     * @throws MismatchedDimensionException
-     * @throws TransformException
-     * @throws FactoryException
-     */
-    protected GeometryWrapper transform(String srsURI, Boolean storeSRSTransform) throws MismatchedDimensionException, TransformException, FactoryException {
-        if (srsInfo.getSrsURI().equals(srsURI)) {
-            return this;
-        }
-
-        return GeometryTransformIndex.transform(this, srsURI, storeSRSTransform);
-    }
-
-    /**
-     * Checks whether the prepared geometry has been initialised.
-     * <br>Done lazily as expensive.
-     */
-    private void checkPreparedGeometry() {
-        if (preparedGeometry == null) {
-            this.preparedGeometry = PreparedGeometryFactory.prepare(xyGeometry);
-        }
-    }
-
-    /**
-     * Returns this geometry wrapper converted to the SRS_URI URI.
-     *
-     * @param srsURI
-     * @return GeometryWrapper after conversion.
-     * @throws FactoryException
-     * @throws MismatchedDimensionException
-     * @throws TransformException
-     */
-    public GeometryWrapper convertSRS(String srsURI) throws FactoryException, MismatchedDimensionException, TransformException {
-        return transform(srsURI);
-    }
-
-    /**
-     *
-     * @return Coordinate/Spatial reference system of the GeometryWrapper.
-     */
-    public CoordinateReferenceSystem getCRS() {
-        return srsInfo.getCrs();
-    }
-
-    /**
-     *
-     * @return Geometry with coordinates in x,y order, regardless of SRS_URI.
-     */
-    public Geometry getXYGeometry() {
-        return xyGeometry;
-    }
-
-    /**
-     *
-     * @return Geometry with coordinates as originally provided.
-     */
-    public Geometry getParsingGeometry() {
-        return parsingGeometry;
-    }
-
-    /**
-     * XY geometry translated by the domain range of the SRS, if a Geographic
-     * SRS.<br>
-     * Returns XY geometry if not a Geographic SRS.
-     *
-     * @return Geometry after translation in X direction.
-     */
-    public Geometry translateXYGeometry() {
-
-        if (translateXYGeometry == null) {
-
-            if (srsInfo.isGeographic()) {
-                double xTranslate = srsInfo.getDomainRangeX();
-                AffineTransformation translation = AffineTransformation.translationInstance(xTranslate, 0);
-                translateXYGeometry = translation.transform(xyGeometry); //Translate seems to be copying Y values into Z and M.
-            } else {
-                translateXYGeometry = xyGeometry;
-            }
-
-        }
-
-        return translateXYGeometry;
-    }
-
-    /**
-     *
-     * @return Coordinate/Spatial reference system URI.
-     */
-    public String getSrsURI() {
-        return srsInfo.getSrsURI();
-    }
-
-    /**
-     *
-     * @return getSRID used in GeoSPARQL Standard page 22 to refer to srsURI.
-     * i.e. getSrsURI and getSRID are the same.
-     */
-    public String getSRID() {
-        return srsInfo.getSrsURI();
-    }
-
-    /**
-     *
-     * @return SRS information that the Geometry Wrapper is using.
-     */
-    public SRSInfo getSrsInfo() {
-        return srsInfo;
-    }
-
-    /**
-     *
-     * @return Whether the SRS URI has been recognised. Operations may fail or
-     * not perform correctly when false.
-     */
-    public Boolean isSRSRecognised() {
-        return srsInfo.isSRSRecognised();
-    }
-
-    /**
-     *
-     * @return Datatype URI of the literal.
-     */
-    public String getGeometryDatatypeURI() {
-        return geometryDatatypeURI;
-    }
-
-    /**
-     *
-     * @return The name of the parsing Geometry's actual class.
-     */
-    public String getGeometryType() {
-        return parsingGeometry.getGeometryType();
-    }
-
-    /**
-     *
-     * @return GeometryDatatype of the literal.
-     */
-    public GeometryDatatype getGeometryDatatype() {
-
-        if (geometryDatatype == null) {
-            geometryDatatype = GeometryDatatype.get(geometryDatatypeURI);
-        }
-        return geometryDatatype;
-    }
-
-    /**
-     * Transformations between SRS and Units will increase the inaccuracy of the
-     * results.
-     *
-     * @param distance
-     * @param targetDistanceUnitsURI
-     * @return Buffer around GeometryWrapper according the provided distance.
-     * @throws FactoryException
-     * @throws MismatchedDimensionException
-     * @throws TransformException
-     */
-    public GeometryWrapper buffer(double distance, String targetDistanceUnitsURI) throws FactoryException, MismatchedDimensionException, TransformException {
-
-        //Check whether the source geometry is linear units for cartesian calculation. If not then transform to relevant UTM SRS GeometryWrapper.
-        Boolean isTargetUnitsLinear = UnitsRegistry.isLinearUnits(targetDistanceUnitsURI);
-        GeometryWrapper transformedGeometryWrapper;
-        Boolean isTransformNeeded;
-
-        if (srsInfo.getUnitsOfMeasure().isLinearUnits() == isTargetUnitsLinear) {
-            //Source geometry and target units are both the same.
-            transformedGeometryWrapper = this;
-            isTransformNeeded = false;
-        } else if (isTargetUnitsLinear) {
-            //Source geometry is not linear but targets are so convert to linear SRS.
-            String sourceUtmURI = getUTMZoneURI();
-            transformedGeometryWrapper = transform(sourceUtmURI);
-            isTransformNeeded = true;
-        } else {
-            //Source geometry is linear but targets are not so convert to nonlinear SRS.
-            transformedGeometryWrapper = transform(SRS_URI.DEFAULT_WKT_CRS84);
-            isTransformNeeded = true;
-        }
-
-        //Check whether the units of the distance need converting.
-        double transformedDistance = UnitsOfMeasure.conversion(distance, targetDistanceUnitsURI, transformedGeometryWrapper.srsInfo.getUnitsOfMeasure().getUnitURI());
-
-        //Buffer the transformed geometry
-        Geometry xyGeo = transformedGeometryWrapper.xyGeometry.buffer(transformedDistance);
-        DimensionInfo bufferedDimensionInfo = new DimensionInfo(dimensionInfo.getCoordinate(), dimensionInfo.getSpatial(), xyGeo.getDimension());
-        Geometry parsingGeo = GeometryReverse.check(xyGeo, transformedGeometryWrapper.srsInfo);
-        GeometryWrapper bufferedGeometryWrapper = new GeometryWrapper(parsingGeo, xyGeo, transformedGeometryWrapper.srsInfo.getSrsURI(), transformedGeometryWrapper.geometryDatatypeURI, bufferedDimensionInfo);
-
-        //Check whether need to transform back to the original srsURI.
-        if (isTransformNeeded) {
-            //Don't store the buffered geometry as it is dependent upon the target distance and so likely to vary beween calls.
-            return bufferedGeometryWrapper.transform(srsInfo.getSrsURI(), false);
-        } else {
-            return bufferedGeometryWrapper;
-        }
-    }
-
-    /**
-     *
-     * @return URI of the GeometryWrapper's UTM zone
-     * @throws FactoryException
-     * @throws MismatchedDimensionException
-     * @throws TransformException
-     */
-    public String getUTMZoneURI() throws FactoryException, MismatchedDimensionException, TransformException {
-
-        if (utmURI == null) {
-
-            //Find a point in the parsing geometry so can directly apply the SRS.
-            Point coord = parsingGeometry.getCentroid();
-            DirectPosition2D point = new DirectPosition2D(coord.getX(), coord.getY());
-
-            //Convert to WGS84. Use WGS84 and not CRS84 as assuming WGS8 is more prevalent.
-            CoordinateReferenceSystem wgs84CRS = SRSRegistry.getCRS(SRS_URI.WGS84_CRS);
-            MathTransform transform = MathTransformRegistry.getMathTransform(srsInfo.getCrs(), wgs84CRS);
-
-            DirectPosition wgs84Point = transform.transform(point, null);
-
-            //Find the UTM zone.
-            utmURI = SRSRegistry.findUTMZoneURIFromWGS84(wgs84Point.getOrdinate(0), wgs84Point.getOrdinate(1));
-
-        }
-        return utmURI;
-    }
-
-    /**
-     * Latitude if Geographic SRS or in WGS84.<br>
-     * Used to convert between linear and non-linear units of measure.
-     *
-     * @return Latitude of Geometry.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public Double getLatitude() throws FactoryException, MismatchedDimensionException, TransformException {
-
-        if (latitude == null) {
-            GeometryWrapper geoGeometryWrapper;
-
-            if (srsInfo.isGeographic()) {
-                //Already a geographic SRS.
-                geoGeometryWrapper = this;
-            } else {
-                //Use WGS84 and not CRS84 as assuming WGS8 is more prevalent.
-                geoGeometryWrapper = convertSRS(SRS_URI.WGS84_CRS);
-            }
-
-            //Latitude is Y-axis.
-            Geometry geometry = geoGeometryWrapper.getXYGeometry();
-            Point point = geometry.getCentroid();
-            latitude = point.getY();
-
-        }
-        return latitude;
-    }
-
-    /**
-     * Distance (Euclidean) defaulting to metres.
-     *
-     * @param targetGeometry
-     * @return Distance
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public double distanceEuclidean(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        return distanceEuclidean(targetGeometry, Unit_URI.METRE_URL);
-    }
-
-    /**
-     * Distance (Euclidean) in the Units of Measure.
-     *
-     * @param targetGeometry
-     * @param unitsOfMeasure
-     * @return Distance
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public double distanceEuclidean(GeometryWrapper targetGeometry, UnitsOfMeasure unitsOfMeasure) throws FactoryException, MismatchedDimensionException, TransformException {
-        return distanceEuclidean(targetGeometry, unitsOfMeasure.getUnitURI());
-    }
-
-    /**
-     * Distance (Euclidean) in the Units of Measure stated in URI.
-     *
-     * @param targetGeometry
-     * @param targetDistanceUnitsURI
-     * @return Distance
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public double distanceEuclidean(GeometryWrapper targetGeometry, String targetDistanceUnitsURI) throws FactoryException, MismatchedDimensionException, TransformException {
-
-        Boolean isUnitsLinear = srsInfo.getUnitsOfMeasure().isLinearUnits();
-        Boolean isTargetUnitsLinear = UnitsRegistry.isLinearUnits(targetDistanceUnitsURI);
-
-        GeometryWrapper transformedTargetGeometry = checkTransformSRS(targetGeometry);
-
-        double distance = xyGeometry.distance(transformedTargetGeometry.xyGeometry);
-        String unitsURI = srsInfo.getUnitsOfMeasure().getUnitURI();
-
-        double targetDistance;
-        if (isUnitsLinear.equals(isTargetUnitsLinear)) {
-            //Units are same so straight conversion.
-            targetDistance = UnitsOfMeasure.conversion(distance, unitsURI, targetDistanceUnitsURI);
-        } else {
-            targetDistance = UnitsOfMeasure.convertBetween(distance, unitsURI, targetDistanceUnitsURI, isTargetUnitsLinear, getLatitude());
-        }
-
-        return targetDistance;
-    }
-
-    /**
-     * Distance (Great Circle) defaulting to metres.
-     *
-     * @param targetGeometry
-     * @return Distance
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public double distanceGreatCircle(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        return distanceGreatCircle(targetGeometry, Unit_URI.METRE_URL);
-    }
-
-    /**
-     * Distance (Great Circle) in the Units of Measure.
-     *
-     * @param targetGeometry
-     * @param unitsOfMeasure
-     * @return Distance
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public double distanceGreatCircle(GeometryWrapper targetGeometry, UnitsOfMeasure unitsOfMeasure) throws FactoryException, MismatchedDimensionException, TransformException {
-        return distanceGreatCircle(targetGeometry, unitsOfMeasure.getUnitURI());
-    }
-
-    /**
-     * Distance (Great Circle) in the Units of Measure stated in URI.
-     *
-     * @param targetGeometry
-     * @param targetDistanceUnitsURI
-     * @return Distance
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public double distanceGreatCircle(GeometryWrapper targetGeometry, String targetDistanceUnitsURI) throws FactoryException, MismatchedDimensionException, TransformException {
-
-        GeometryWrapper transformedSourceGeometry;
-        if (srsInfo.isGeographic()) {
-            //Already a geographic SRS.
-            transformedSourceGeometry = this;
-        } else {
-            //Use WGS84 and not CRS84 as assuming WGS8 is more prevalent.
-            transformedSourceGeometry = this.transform(SRS_URI.WGS84_CRS);
-        }
-
-        GeometryWrapper transformedTargetGeometry = transformedSourceGeometry.checkTransformSRS(targetGeometry);
-
-        //Find the nearest pair of coordinates from each Geometry using Euclidean distance (adjusting for wrap around).
-        //These are then used for Great Circle distance.
-        CoordinatePair coordinatePair = CoordinatePair.findNearestPair(transformedSourceGeometry, transformedTargetGeometry);
-
-        //Check whether the nearest pair are the same, i.e. the overlap or within each other.
-        if (coordinatePair.isEqual()) {
-            //Exit early as the distance is zero.
-            return 0.0;
-        }
-
-        Coordinate coord1 = coordinatePair.getCoord1();
-        Coordinate coord2 = coordinatePair.getCoord2();
-
-        double distance = GreatCircleDistance.haversineFormula(coord1.getY(), coord1.getX(), coord2.getY(), coord2.getX());
-
-        //Convert the Great Circle distance from metres into the requested units.
-        Boolean isTargetUnitsLinear = UnitsRegistry.isLinearUnits(targetDistanceUnitsURI);
-        double targetDistance;
-        if (isTargetUnitsLinear) {
-            //Target units are linear so straight conversion. Distance is in metres already.
-            targetDistance = UnitsOfMeasure.conversion(distance, Unit_URI.METRE_URL, targetDistanceUnitsURI);
-        } else {
-            targetDistance = UnitsOfMeasure.convertBetween(distance, Unit_URI.METRE_URL, targetDistanceUnitsURI, isTargetUnitsLinear, transformedSourceGeometry.getLatitude());
-        }
-
-        return targetDistance;
-    }
-
-    /**
-     * Distance (Euclidean or Great Circle depending on Geometry SRS URI)
-     * defaulting to metres.
-     *
-     * @param targetGeometry
-     * @return Distance
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public double distance(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        return distance(targetGeometry, Unit_URI.METRE_URL);
-    }
-
-    /**
-     * Distance (Euclidean or Great Circle depending on Geometry SRS URI) in the
-     * Units of Measure.
-     *
-     * @param targetGeometry
-     * @param unitsOfMeasure
-     * @return Distance
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public double distance(GeometryWrapper targetGeometry, UnitsOfMeasure unitsOfMeasure) throws FactoryException, MismatchedDimensionException, TransformException {
-        return distance(targetGeometry, unitsOfMeasure.getUnitURI());
-    }
-
-    /**
-     * Distance (Euclidean or Great Circle depending on Geometry SRS URI) in the
-     * Units of Measure stated in URI.
-     *
-     * @param targetGeometry
-     * @param targetDistanceUnitsURI
-     * @return Distance
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public double distance(GeometryWrapper targetGeometry, String targetDistanceUnitsURI) throws FactoryException, MismatchedDimensionException, TransformException {
-
-        double targetDistance;
-        if (srsInfo.isGeographic()) {
-            targetDistance = distanceGreatCircle(targetGeometry, targetDistanceUnitsURI);
-        } else {
-            targetDistance = distanceEuclidean(targetGeometry, targetDistanceUnitsURI);
-        }
-
-        return targetDistance;
-    }
-
-    /**
-     *
-     * @return Boundary of GeometryWrapper
-     */
-    public GeometryWrapper boundary() {
-        Geometry xyGeo = this.xyGeometry.getBoundary();
-        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
-        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo);
-    }
-
-    /**
-     *
-     * @return Convex Hull of GeometryWrapper
-     */
-    public GeometryWrapper convexHull() {
-        Geometry xyGeo = this.xyGeometry.convexHull();
-        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
-        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return Difference of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public GeometryWrapper difference(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        Geometry xyGeo = this.xyGeometry.difference(transformedGeometry.xyGeometry);
-        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
-        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo);
-    }
-
-    /**
-     * Envelope of GeometryWrapper with original coordinate order.
-     *
-     * @return Envelope of GeometryWrapper
-     */
-    public GeometryWrapper envelope() {
-        GeometryFactory geometryFactory = this.xyGeometry.getFactory();
-        Envelope xyEnvelope = this.getEnvelope();
-        Geometry xyGeo = geometryFactory.toGeometry(xyEnvelope);
-        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
-        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo);
-    }
-
-    /**
-     * Envelope of GeometryWrapper in XY order.
-     *
-     * @return Envelope of GeometryWrapper
-     */
-    public Envelope getEnvelope() {
-        if (envelope == null) {
-            envelope = this.xyGeometry.getEnvelopeInternal();
-        }
-
-        return envelope;
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return Intersection of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public GeometryWrapper intersection(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        Geometry xyGeo = this.xyGeometry.intersection(transformedGeometry.xyGeometry);
-        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
-        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return Intersection Matrix of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public IntersectionMatrix relate(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return xyGeometry.relate(transformedGeometry.xyGeometry);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @param intersectionPattern
-     * @return Relation of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public boolean relate(GeometryWrapper targetGeometry, String intersectionPattern) throws FactoryException, MismatchedDimensionException, TransformException {
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return xyGeometry.relate(transformedGeometry.xyGeometry, intersectionPattern);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return Symmetric Difference of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public GeometryWrapper symDifference(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        Geometry xyGeo = this.xyGeometry.symDifference(transformedGeometry.xyGeometry);
-        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
-        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo, null);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return Union of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public GeometryWrapper union(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        Geometry xyGeo = this.xyGeometry.union(transformedGeometry.xyGeometry);
-        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
-        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo, null);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return sfContains of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public boolean contains(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        this.checkPreparedGeometry();
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return this.preparedGeometry.contains(transformedGeometry.xyGeometry);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return sfCrosses of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public boolean crosses(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        this.checkPreparedGeometry();
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return this.preparedGeometry.crosses(transformedGeometry.xyGeometry);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return sfDisjoint of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public boolean disjoint(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        this.checkPreparedGeometry();
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return this.preparedGeometry.disjoint(transformedGeometry.xyGeometry);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return Topology equals of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public boolean equalsTopo(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return this.xyGeometry.equalsTopo(transformedGeometry.xyGeometry);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return Equals exactly of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public boolean equalsExact(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return this.xyGeometry.equalsExact(transformedGeometry.xyGeometry);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @param tolerance
-     * @return Equals exactly of GeometryWrapper with target using provided
-     * tolerance.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public boolean equalsExact(GeometryWrapper targetGeometry, double tolerance) throws FactoryException, MismatchedDimensionException, TransformException {
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return this.xyGeometry.equalsExact(transformedGeometry.xyGeometry, tolerance);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return sfIntersects of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public boolean intersects(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        this.checkPreparedGeometry();
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return this.preparedGeometry.intersects(transformedGeometry.xyGeometry);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return sfOverlaps of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public boolean overlaps(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        this.checkPreparedGeometry();
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return this.preparedGeometry.overlaps(transformedGeometry.xyGeometry);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return sfTouches of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public boolean touches(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        this.checkPreparedGeometry();
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return this.preparedGeometry.touches(transformedGeometry.xyGeometry);
-    }
-
-    /**
-     *
-     * @param targetGeometry
-     * @return sfWithin of GeometryWrapper with target.
-     * @throws org.opengis.util.FactoryException
-     * @throws org.opengis.referencing.operation.TransformException
-     */
-    public boolean within(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
-        this.checkPreparedGeometry();
-        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
-        return this.preparedGeometry.within(transformedGeometry.xyGeometry);
-    }
-
-    /**
-     *
-     * @return GeometryWrapper as NodeValue
-     */
-    public NodeValue asNodeValue() throws DatatypeFormatException {
-        Literal literal = asLiteral();
-        return NodeValue.makeNode(literal.getLexicalForm(), literal.getDatatype());
-    }
-
-    /**
-     *
-     * @return GeometryWrapper as Node
-     */
-    public Node asNode() throws DatatypeFormatException {
-        return asNodeValue().asNode();
-    }
-
-    /**
-     *
-     * @return GeometryWrapper as Literal
-     */
-    public Literal asLiteral() throws DatatypeFormatException {
-
-        GeometryDatatype datatype = getGeometryDatatype(); //Datatype is only retrieved when required.
-        if (lexicalForm != null) {
-            return ResourceFactory.createTypedLiteral(lexicalForm, datatype);
-        }
-
-        Literal literal = asLiteral(datatype);
-        lexicalForm = literal.getLexicalForm();
-        return literal;
-    }
-
-    /**
-     *
-     * @param outputGeometryDatatypeURI
-     * @return GeometryWrapper as Literal in datatype form.
-     */
-    public Literal asLiteral(String outputGeometryDatatypeURI) throws DatatypeFormatException {
-        GeometryDatatype datatype = GeometryDatatype.get(outputGeometryDatatypeURI);
-        return asLiteral(datatype);
-    }
-
-    /**
-     *
-     * @param datatype
-     * @return GeometryWrapper as Literal
-     */
-    public Literal asLiteral(GeometryDatatype datatype) {
-        String tempLexicalForm = datatype.unparse(this);
-        return ResourceFactory.createTypedLiteral(tempLexicalForm, datatype);
-    }
-
-    /**
-     *
-     * @return Coordinate dimension, i.e. 2 (x,y), 3 (x,y,z or x,y,m) or 4
-     * (x,y,z,m)
-     */
-    public int getCoordinateDimension() {
-        return dimensionInfo.getCoordinate();
-    }
-
-    /**
-     *
-     * @return Spatial dimension, i.e. 2 or 3
-     */
-    public int getSpatialDimension() {
-        return dimensionInfo.getSpatial();
-    }
-
-    /**
-     *
-     * @return Topological dimension, i.e. 0, 1 or 2
-     */
-    public int getTopologicalDimension() {
-        return dimensionInfo.getTopological();
-    }
-
-    /**
-     *
-     * @return Enum of coordinate dimensions.
-     */
-    public CoordinateSequenceDimensions getCoordinateSequenceDimensions() {
-        return dimensionInfo.getDimensions();
-    }
-
-    /**
-     *
-     * @return Units of Measure for the GeometryWrapper SRS.
-     */
-    public UnitsOfMeasure getUnitsOfMeasure() {
-        return srsInfo.getUnitsOfMeasure();
-    }
-
-    /**
-     *
-     * @return GeometryWrapper's coordinate, spatial and topological dimensions.
-     */
-    public DimensionInfo getDimensionInfo() {
-        return dimensionInfo;
-    }
-
-    /**
-     *
-     * @return String literal of Geometry Wrapper.
-     */
-    public String getLexicalForm() {
-
-        if (lexicalForm != null) {
-            return lexicalForm;
-        } else {
-            Literal literal = asLiteral();
-            return literal.getLexicalForm();
-        }
-    }
-
-    /**
-     *
-     * @return Geometry is empty of coordinates.
-     */
-    public boolean isEmpty() {
-        return this.xyGeometry.isEmpty();
-    }
-
-    /**
-     *
-     * @return Geometry is in simple form.
-     */
-    public boolean isSimple() {
-        return this.xyGeometry.isSimple();
-    }
-
-    /**
-     *
-     * @return Geometry is topologically valid.
-     */
-    public boolean isValid() {
-        return this.xyGeometry.isValid();
-    }
-
-    /**
-     * Extract Geometry Wrapper from Geometry Literal.
-     *
-     * @param geometryLiteral
-     * @param targetIndex
-     * @return Geometry Wrapper of the Geometry Literal.
-     */
-    public static final GeometryWrapper extract(NodeValue geometryLiteral, GeometryIndex targetIndex) {
-
-        Node node = geometryLiteral.asNode();
-
-        return extract(node, targetIndex);
-    }
-
-    /**
-     * Extract Geometry Wrapper from Geometry Literal.
-     *
-     * @param geometryLiteral
-     * @param targetIndex
-     * @return Geometry Wrapper of the Geometry Literal.
-     */
-    public static final GeometryWrapper extract(Node geometryLiteral, GeometryIndex targetIndex) throws DatatypeFormatException {
-
-        if (!geometryLiteral.isLiteral()) {
-            throw new DatatypeFormatException("Not a Literal: " + geometryLiteral);
-        }
-
-        String datatypeURI = geometryLiteral.getLiteralDatatypeURI();
-        String lexicalForm = geometryLiteral.getLiteralLexicalForm();
-        return extract(lexicalForm, datatypeURI, targetIndex);
-    }
-
-    /**
-     * Extract Geometry Wrapper from Geometry Literal.
-     *
-     * @param geometryLiteral
-     * @return Geometry Wrapper of the Geometry Literal.
-     */
-    public static final GeometryWrapper extract(NodeValue geometryLiteral) {
-        return extract(geometryLiteral, GeometryIndex.PRIMARY);
-    }
-
-    /**
-     * Extract Geometry Wrapper from Geometry Literal.
-     *
-     * @param geometryLiteral
-     * @return Geometry Wrapper of the Geometry Literal.
-     */
-    public static final GeometryWrapper extract(Node geometryLiteral) {
-        return extract(geometryLiteral, GeometryIndex.PRIMARY);
-    }
-
-    /**
-     * Extract Geometry Wrapper from Geometry Literal. Returns null if invalid
-     * literal provided.
-     *
-     * @param geometryLiteral
-     * @param targetIndex
-     * @return Geometry Wrapper of the Geometry Literal.
-     */
-    public static final GeometryWrapper extract(Literal geometryLiteral, GeometryIndex targetIndex) {
-        return extract(geometryLiteral.getLexicalForm(), geometryLiteral.getDatatypeURI(), targetIndex);
-    }
-
-    /**
-     * Extract Geometry Wrapper from Geometry Literal.
-     *
-     * @param geometryLiteral
-     * @return Geometry Wrapper of the Geometry Literal.
-     */
-    public static final GeometryWrapper extract(Literal geometryLiteral) {
-        return extract(geometryLiteral, GeometryIndex.PRIMARY);
-    }
-
-    /**
-     * Extract Geometry Wrapper from Geometry Literal.
-     *
-     * @param lexicalForm
-     * @param datatypeURI
-     * @return Geometry Wrapper of the Geometry Literal.
-     */
-    public static GeometryWrapper extract(String lexicalForm, String datatypeURI) {
-        return extract(lexicalForm, datatypeURI, GeometryIndex.PRIMARY);
-    }
-
-    /**
-     * Extract Geometry Wrapper from Geometry Literal.
-     *
-     * @param lexicalForm
-     * @param datatypeURI
-     * @param targetIndex
-     * @return Geometry Wrapper of the Geometry Literal.
-     */
-    public static GeometryWrapper extract(String lexicalForm, String datatypeURI, GeometryIndex targetIndex) throws DatatypeFormatException {
-
-        if (lexicalForm == null || datatypeURI == null) {
-            throw new DatatypeFormatException("GeometryWrapper extraction: arguments cannot be null - " + lexicalForm + ", " + datatypeURI);
-        }
-
-        GeometryDatatype datatype = GeometryDatatype.get(datatypeURI);
-        GeometryWrapper geometry = datatype.parse(lexicalForm, targetIndex);
-        return geometry;
-    }
-
-    /**
-     * Builds a WKT Point of Geometry Wrapper.<br>
-     * This method does not use the GeometryLiteralIndex and so is best used for
-     * one of Geometry Wrappers.
-     *
-     * @return Geometry Wrapper of WKT Point.
-     */
-    public static final GeometryWrapper fromPoint(double x, double y, String srsURI) {
-        CustomCoordinateSequence coordSequence = CustomCoordinateSequence.createPoint(x, y);
-        GeometryWrapper geometryWrapper = new GeometryWrapper(coordSequence, WKTDatatype.URI, srsURI);
-        return geometryWrapper;
-    }
-
-    /**
-     *
-     * @return Empty GeometryWrapper in WKT datatype.
-     */
-    public static final GeometryWrapper getEmptyWKT() {
-        return WKTDatatype.INSTANCE.read("");
-    }
-
-    /**
-     *
-     * @return Empty GeometryWrapper in GML datatype.
-     */
-    public static final GeometryWrapper getEmptyGML() {
-        return GMLDatatype.INSTANCE.read("");
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 3;
-        hash = 23 * hash + Objects.hashCode(this.dimensionInfo);
-        hash = 23 * hash + Objects.hashCode(this.srsInfo);
-        hash = 23 * hash + Objects.hashCode(this.xyGeometry);
-        hash = 23 * hash + Objects.hashCode(this.geometryDatatypeURI);
-        return hash;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final GeometryWrapper other = (GeometryWrapper) obj;
-        if (!Objects.equals(this.geometryDatatypeURI, other.geometryDatatypeURI)) {
-            return false;
-        }
-        if (!Objects.equals(this.dimensionInfo, other.dimensionInfo)) {
-            return false;
-        }
-        if (!Objects.equals(this.srsInfo, other.srsInfo)) {
-            return false;
-        }
-        return Objects.equals(this.xyGeometry, other.xyGeometry);
-    }
-
-    @Override
-    public String toString() {
-        return "GeometryWrapper{" + "dimensionInfo=" + dimensionInfo + ", geometryDatatypeURI=" + geometryDatatypeURI + ", lexicalForm=" + lexicalForm + ", utmURI=" + utmURI + ", latitude=" + latitude + ", xyGeometry=" + xyGeometry + ", parsingGeometry=" + parsingGeometry + ", preparedGeometry=" + preparedGeometry + ", srsInfo=" + srsInfo + '}';
-    }
-
-}
+/*
+ * 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;
+
+import java.io.Serializable;
+import java.lang.invoke.MethodHandles;
+import java.util.Objects;
+import org.apache.jena.datatypes.DatatypeFormatException;
+import org.apache.jena.geosparql.configuration.GeoSPARQLConfig;
+import org.apache.jena.geosparql.implementation.datatype.GMLDatatype;
+import org.apache.jena.geosparql.implementation.datatype.GeometryDatatype;
+import org.apache.jena.geosparql.implementation.datatype.WKTDatatype;
+import org.apache.jena.geosparql.implementation.great_circle.CoordinatePair;
+import org.apache.jena.geosparql.implementation.great_circle.GreatCircleDistance;
+import org.apache.jena.geosparql.implementation.index.GeometryLiteralIndex.GeometryIndex;
+import org.apache.jena.geosparql.implementation.index.GeometryTransformIndex;
+import org.apache.jena.geosparql.implementation.jts.CoordinateSequenceDimensions;
+import org.apache.jena.geosparql.implementation.jts.CustomCoordinateSequence;
+import org.apache.jena.geosparql.implementation.jts.CustomGeometryFactory;
+import org.apache.jena.geosparql.implementation.registry.MathTransformRegistry;
+import org.apache.jena.geosparql.implementation.registry.SRSRegistry;
+import org.apache.jena.geosparql.implementation.registry.UnitsRegistry;
+import org.apache.jena.geosparql.implementation.vocabulary.SRS_URI;
+import org.apache.jena.geosparql.implementation.vocabulary.Unit_URI;
+import org.apache.jena.graph.Node;
+import org.apache.jena.rdf.model.Literal;
+import org.apache.jena.rdf.model.ResourceFactory;
+import org.apache.jena.sparql.expr.NodeValue;
+import org.apache.sis.geometry.DirectPosition2D;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Envelope;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.IntersectionMatrix;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.prep.PreparedGeometry;
+import org.locationtech.jts.geom.prep.PreparedGeometryFactory;
+import org.locationtech.jts.geom.util.AffineTransformation;
+import org.opengis.geometry.DirectPosition;
+import org.opengis.geometry.MismatchedDimensionException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ *
+ */
+public class GeometryWrapper implements Serializable {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    private final DimensionInfo dimensionInfo;
+    private final SRSInfo srsInfo;
+    private final Geometry xyGeometry;
+    private final Geometry parsingGeometry;
+    private PreparedGeometry preparedGeometry;
+    private Envelope envelope;
+    private Geometry translateXYGeometry;
+    private final String geometryDatatypeURI;
+    private GeometryDatatype geometryDatatype;
+    private String lexicalForm;
+    private String utmURI = null;
+    private Double latitude = null;
+
+    /**
+     *
+     * @param geometry In X/Y or Y/X coordinate order of the SRS URI.
+     * @param srsURI
+     * @param geometryDatatypeURI
+     * @param dimensionInfo
+     */
+    public GeometryWrapper(Geometry geometry, String srsURI, String geometryDatatypeURI, DimensionInfo dimensionInfo) {
+        this(geometry, srsURI, geometryDatatypeURI, dimensionInfo, null);
+    }
+
+    /**
+     *
+     * @param geometry In X/Y or Y/X coordinate order of the SRS URI.
+     * @param srsURI
+     * @param geometryDatatypeURI
+     * @param dimensionInfo
+     * @param geometryLiteral
+     */
+    public GeometryWrapper(Geometry geometry, String srsURI, String geometryDatatypeURI, DimensionInfo dimensionInfo, String geometryLiteral) {
+        this(geometry, GeometryReverse.check(geometry, srsURI.isEmpty() ? SRS_URI.DEFAULT_WKT_CRS84 : srsURI), srsURI.isEmpty() ? SRS_URI.DEFAULT_WKT_CRS84 : srsURI, geometryDatatypeURI, dimensionInfo, geometryLiteral);
+    }
+
+    protected GeometryWrapper(Geometry parsingGeometry, Geometry xyGeometry, String srsURI, String geometryDatatypeURI, DimensionInfo dimensionInfo) {
+        this(parsingGeometry, xyGeometry, srsURI, geometryDatatypeURI, dimensionInfo, null);
+    }
+
+    protected GeometryWrapper(Geometry parsingGeometry, Geometry xyGeometry, String srsURI, String geometryDatatypeURI, DimensionInfo dimensionInfo, String lexicalForm) {
+
+        this.parsingGeometry = parsingGeometry;
+        this.xyGeometry = xyGeometry;
+        this.preparedGeometry = null; //Initialised when required by spatial relations checkPreparedGeometry.
+        this.envelope = null; //Initialised when required by getEnvelope().
+        this.translateXYGeometry = null; //Initialised when required by translateGeometry().
+        this.geometryDatatypeURI = geometryDatatypeURI;
+        this.geometryDatatype = null; //Inilialised when required by getGeometryDatatype().
+
+        if (srsURI.isEmpty()) {
+            srsURI = SRS_URI.DEFAULT_WKT_CRS84;
+        }
+
+        this.srsInfo = SRSRegistry.getSRSInfo(srsURI);
+
+        this.dimensionInfo = dimensionInfo;
+        this.lexicalForm = lexicalForm; //If not Initialised then required by asLiteral() etc.
+    }
+
+    /**
+     * Default to WGS84 geometry and XY coordinate dimensions.
+     *
+     * @param geometry In X/Y or Y/X coordinate order of WGS84.
+     * @param geometryDatatypeURI
+     */
+    public GeometryWrapper(Geometry geometry, String geometryDatatypeURI) {
+        this(geometry, "", geometryDatatypeURI, DimensionInfo.XY_POINT);
+    }
+
+    /**
+     * Default to XY coordinate dimensions.
+     *
+     * @param geometry In X/Y or Y/X coordinate order of the SRS URI.
+     * @param srsURI
+     * @param geometryDatatypeURI
+     */
+    public GeometryWrapper(Geometry geometry, String srsURI, String geometryDatatypeURI) {
+        this(geometry, srsURI, geometryDatatypeURI, DimensionInfo.XY_POINT);
+    }
+
+    /**
+     * Empty geometry with specified parameters.
+     *
+     * @param srsURI
+     * @param geometryDatatypeURI
+     */
+    public GeometryWrapper(String srsURI, String geometryDatatypeURI) {
+        this(new CustomCoordinateSequence(DimensionInfo.XY_POINT.getDimensions()), geometryDatatypeURI, srsURI);
+    }
+
+    /**
+     * Point geometry with specified SRS.
+     *
+     * @param pointCoordinateSequence
+     * @param geometryDatatypeURI
+     * @param srsURI
+     */
+    public GeometryWrapper(CustomCoordinateSequence pointCoordinateSequence, String geometryDatatypeURI, String srsURI) {
+        this(CustomGeometryFactory.theInstance().createPoint(pointCoordinateSequence), srsURI, geometryDatatypeURI, DimensionInfo.XY_POINT);
+    }
+
+    /**
+     * Copy GeometryWrapper.
+     *
+     * @param geometryWrapper
+     */
+    public GeometryWrapper(GeometryWrapper geometryWrapper) {
+
+        this.xyGeometry = geometryWrapper.xyGeometry;
+        this.parsingGeometry = geometryWrapper.parsingGeometry;
+        this.preparedGeometry = geometryWrapper.preparedGeometry;
+        this.envelope = geometryWrapper.envelope;
+        this.translateXYGeometry = geometryWrapper.translateXYGeometry;
+        this.utmURI = geometryWrapper.utmURI;
+        this.latitude = geometryWrapper.latitude;
+        this.geometryDatatypeURI = geometryWrapper.geometryDatatypeURI;
+        this.geometryDatatype = geometryWrapper.geometryDatatype;
+
+        this.srsInfo = geometryWrapper.srsInfo;
+        this.dimensionInfo = geometryWrapper.dimensionInfo;
+        this.lexicalForm = geometryWrapper.lexicalForm;
+    }
+
+    /**
+     * Transforms, if necessary, the provided target GeometryWrapper according
+     * to this GeometryWrapper SRS_URI.
+     *
+     * @param targetGeometryWrapper
+     * @return GeometryWrapper after transformation.
+     * @throws FactoryException
+     * @throws MismatchedDimensionException
+     * @throws TransformException
+     */
+    public GeometryWrapper checkTransformSRS(GeometryWrapper targetGeometryWrapper) throws FactoryException, MismatchedDimensionException, TransformException {
+
+        GeometryWrapper transformedGeometryWrapper;
+        String srsURI = srsInfo.getSrsURI();
+        if (srsURI.equals(targetGeometryWrapper.srsInfo.getSrsURI())) {
+            transformedGeometryWrapper = targetGeometryWrapper;
+        } else {
+            if (!GeoSPARQLConfig.ALLOW_GEOMETRY_SRS_TRANSFORMATION) {
+                throw new TransformException("GeometryLiteral SRS transformation is disabled, see GeoSPARQLConfig.allowGeometrySRSTransformation(...).");
+            }
+
+            transformedGeometryWrapper = targetGeometryWrapper.transform(srsURI);
+        }
+
+        return transformedGeometryWrapper;
+    }
+
+    /**
+     * Transform the GeometryWrapper into another spatial reference system.<br>
+     *
+     * @param srsURI
+     * @return New GeometryWrapper after transformation, or this GeometryWrapper
+     * if no transformation.
+     * @throws MismatchedDimensionException
+     * @throws TransformException
+     * @throws FactoryException
+     */
+    public GeometryWrapper transform(String srsURI) throws MismatchedDimensionException, TransformException, FactoryException {
+        return transform(srsURI, true);
+    }
+
+    /**
+     * Transform the GeometryWrapper into another spatial reference system.<br>
+     *
+     * @param srsInfo
+     * @return New GeometryWrapper after transformation, or this GeometryWrapper
+     * if no transformation.
+     * @throws MismatchedDimensionException
+     * @throws TransformException
+     * @throws FactoryException
+     */
+    public GeometryWrapper transform(SRSInfo srsInfo) throws MismatchedDimensionException, TransformException, FactoryException {
+        return transform(srsInfo.getSrsURI(), true);
+    }
+
+    /**
+     * Transform the GeometryWrapper into another spatial reference system.<br>
+     * Option to store the resulting GeometryWrapper in the index.
+     *
+     * @param srsURI
+     * @param storeSRSTransform
+     * @return GeometryWrapper after transformation.
+     * @throws MismatchedDimensionException
+     * @throws TransformException
+     * @throws FactoryException
+     */
+    protected GeometryWrapper transform(String srsURI, Boolean storeSRSTransform) throws MismatchedDimensionException, TransformException, FactoryException {
+        if (srsInfo.getSrsURI().equals(srsURI)) {
+            return this;
+        }
+
+        return GeometryTransformIndex.transform(this, srsURI, storeSRSTransform);
+    }
+
+    /**
+     * Checks whether the prepared geometry has been initialised.
+     * <br>Done lazily as expensive.
+     */
+    private void checkPreparedGeometry() {
+        if (preparedGeometry == null) {
+            this.preparedGeometry = PreparedGeometryFactory.prepare(xyGeometry);
+        }
+    }
+
+    /**
+     * Returns this geometry wrapper converted to the SRS_URI URI.
+     *
+     * @param srsURI
+     * @return GeometryWrapper after conversion.
+     * @throws FactoryException
+     * @throws MismatchedDimensionException
+     * @throws TransformException
+     */
+    public GeometryWrapper convertSRS(String srsURI) throws FactoryException, MismatchedDimensionException, TransformException {
+        return transform(srsURI);
+    }
+
+    /**
+     *
+     * @return Coordinate/Spatial reference system of the GeometryWrapper.
+     */
+    public CoordinateReferenceSystem getCRS() {
+        return srsInfo.getCrs();
+    }
+
+    /**
+     *
+     * @return Geometry with coordinates in x,y order, regardless of SRS_URI.
+     */
+    public Geometry getXYGeometry() {
+        return xyGeometry;
+    }
+
+    /**
+     *
+     * @return Geometry with coordinates as originally provided.
+     */
+    public Geometry getParsingGeometry() {
+        return parsingGeometry;
+    }
+
+    /**
+     * XY geometry translated by the domain range of the SRS, if a Geographic
+     * SRS.<br>
+     * Returns XY geometry if not a Geographic SRS.
+     *
+     * @return Geometry after translation in X direction.
+     */
+    public Geometry translateXYGeometry() {
+
+        if (translateXYGeometry == null) {
+
+            if (srsInfo.isGeographic()) {
+                double xTranslate = srsInfo.getDomainRangeX();
+                AffineTransformation translation = AffineTransformation.translationInstance(xTranslate, 0);
+                translateXYGeometry = translation.transform(xyGeometry); //Translate seems to be copying Y values into Z and M.
+            } else {
+                translateXYGeometry = xyGeometry;
+            }
+
+        }
+
+        return translateXYGeometry;
+    }
+
+    /**
+     *
+     * @return Coordinate/Spatial reference system URI.
+     */
+    public String getSrsURI() {
+        return srsInfo.getSrsURI();
+    }
+
+    /**
+     *
+     * @return getSRID used in GeoSPARQL Standard page 22 to refer to srsURI.
+     * i.e. getSrsURI and getSRID are the same.
+     */
+    public String getSRID() {
+        return srsInfo.getSrsURI();
+    }
+
+    /**
+     *
+     * @return SRS information that the Geometry Wrapper is using.
+     */
+    public SRSInfo getSrsInfo() {
+        return srsInfo;
+    }
+
+    /**
+     *
+     * @return Whether the SRS URI has been recognised. Operations may fail or
+     * not perform correctly when false.
+     */
+    public Boolean isSRSRecognised() {
+        return srsInfo.isSRSRecognised();
+    }
+
+    /**
+     *
+     * @return Datatype URI of the literal.
+     */
+    public String getGeometryDatatypeURI() {
+        return geometryDatatypeURI;
+    }
+
+    /**
+     *
+     * @return The name of the parsing Geometry's actual class.
+     */
+    public String getGeometryType() {
+        return parsingGeometry.getGeometryType();
+    }
+
+    /**
+     *
+     * @return GeometryDatatype of the literal.
+     */
+    public GeometryDatatype getGeometryDatatype() {
+
+        if (geometryDatatype == null) {
+            geometryDatatype = GeometryDatatype.get(geometryDatatypeURI);
+        }
+        return geometryDatatype;
+    }
+
+    /**
+     * Transformations between SRS and Units will increase the inaccuracy of the
+     * results.
+     *
+     * @param distance
+     * @param targetDistanceUnitsURI
+     * @return Buffer around GeometryWrapper according the provided distance.
+     * @throws FactoryException
+     * @throws MismatchedDimensionException
+     * @throws TransformException
+     * @throws UnitsConversionException
+     */
+    public GeometryWrapper buffer(double distance, String targetDistanceUnitsURI) throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
+
+        //Check whether the source geometry is linear units for cartesian calculation. If not then transform to relevant UTM SRS GeometryWrapper.
+        Boolean isTargetUnitsLinear = UnitsRegistry.isLinearUnits(targetDistanceUnitsURI);
+
+        if (srsInfo.getUnitsOfMeasure().isLinearUnits() != isTargetUnitsLinear) {
+            //Source geometry and target units are not the same.
+            throw new UnitsConversionException("Buffer does not support conversion between linear and non-linear units.");
+        }
+
+        //Transform distance into current units.
+        double transformedDistance = UnitsOfMeasure.conversion(distance, targetDistanceUnitsURI, this.srsInfo.getUnitsOfMeasure().getUnitURI());
+
+        //Buffer the geometry by the transformed distance.
+        Geometry xyGeo = this.xyGeometry.buffer(transformedDistance);
+        DimensionInfo bufferedDimensionInfo = new DimensionInfo(dimensionInfo.getCoordinate(), dimensionInfo.getSpatial(), xyGeo.getDimension());
+        Geometry parsingGeo = GeometryReverse.check(xyGeo, this.srsInfo);
+        return new GeometryWrapper(parsingGeo, xyGeo, this.srsInfo.getSrsURI(), this.geometryDatatypeURI, bufferedDimensionInfo);
+    }
+
+    /**
+     *
+     * @return URI of the GeometryWrapper's UTM zone
+     * @throws FactoryException
+     * @throws MismatchedDimensionException
+     * @throws TransformException
+     */
+    public String getUTMZoneURI() throws FactoryException, MismatchedDimensionException, TransformException {
+
+        if (utmURI == null) {
+
+            //Find a point in the parsing geometry so can directly apply the SRS.
+            Point coord = parsingGeometry.getCentroid();
+            DirectPosition2D point = new DirectPosition2D(coord.getX(), coord.getY());
+
+            //Convert to WGS84. Use WGS84 and not CRS84 as assuming WGS8 is more prevalent.
+            CoordinateReferenceSystem wgs84CRS = SRSRegistry.getCRS(SRS_URI.WGS84_CRS);
+            MathTransform transform = MathTransformRegistry.getMathTransform(srsInfo.getCrs(), wgs84CRS);
+            
+            DirectPosition wgs84Point = transform.transform(point, null);
+            
+            //Find the UTM zone.
+            utmURI = SRSRegistry.findUTMZoneURIFromWGS84(wgs84Point.getOrdinate(0), wgs84Point.getOrdinate(1));
+
+        }
+        return utmURI;
+    }
+
+    /**
+     * Latitude if Geographic SRS or in WGS84.<br>
+     * Used to convert between linear and non-linear units of measure.
+     *
+     * @return Latitude of Geometry.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public Double getLatitude() throws FactoryException, MismatchedDimensionException, TransformException {
+
+        if (latitude == null) {
+            GeometryWrapper geoGeometryWrapper;
+
+            if (srsInfo.isGeographic()) {
+                //Already a geographic SRS.
+                geoGeometryWrapper = this;
+            } else {
+                //Use WGS84 and not CRS84 as assuming WGS8 is more prevalent.
+                geoGeometryWrapper = convertSRS(SRS_URI.WGS84_CRS);
+            }
+
+            //Latitude is Y-axis.
+            Geometry geometry = geoGeometryWrapper.getXYGeometry();
+            Point point = geometry.getCentroid();
+            latitude = point.getY();
+
+        }
+        return latitude;
+    }
+
+    /**
+     * Distance (Euclidean) defaulting to metres.
+     *
+     * @param targetGeometry
+     * @return Distance
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     * @throws UnitsConversionException
+     */
+    public double distanceEuclidean(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
+        return distanceEuclidean(targetGeometry, Unit_URI.METRE_URL);
+    }
+
+    /**
+     * Distance (Euclidean) in the Units of Measure.
+     *
+     * @param targetGeometry
+     * @param unitsOfMeasure
+     * @return Distance
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     * @throws UnitsConversionException
+     */
+    public double distanceEuclidean(GeometryWrapper targetGeometry, UnitsOfMeasure unitsOfMeasure) throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
+        return distanceEuclidean(targetGeometry, unitsOfMeasure.getUnitURI());
+    }
+
+    /**
+     * Distance (Euclidean) in the Units of Measure stated in URI.
+     *
+     * @param targetGeometry
+     * @param targetDistanceUnitsURI
+     * @return Distance
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     * @throws UnitsConversionException
+     */
+    public double distanceEuclidean(GeometryWrapper targetGeometry, String targetDistanceUnitsURI) throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
+
+        GeometryWrapper transformedTargetGeometry = checkTransformSRS(targetGeometry);
+
+        double distance = xyGeometry.distance(transformedTargetGeometry.xyGeometry);
+        String unitsURI = srsInfo.getUnitsOfMeasure().getUnitURI();
+        return UnitsOfMeasure.conversion(distance, unitsURI, targetDistanceUnitsURI);
+    }
+
+    /**
+     * Distance (Great Circle) defaulting to metres.
+     *
+     * @param targetGeometry
+     * @return Distance
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     * @throws UnitsConversionException
+     */
+    public double distanceGreatCircle(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
+        return distanceGreatCircle(targetGeometry, Unit_URI.METRE_URL);
+    }
+
+    /**
+     * Distance (Great Circle) in the Units of Measure.
+     *
+     * @param targetGeometry
+     * @param unitsOfMeasure
+     * @return Distance
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     * @throws UnitsConversionException
+     */
+    public double distanceGreatCircle(GeometryWrapper targetGeometry, UnitsOfMeasure unitsOfMeasure) throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
+        return distanceGreatCircle(targetGeometry, unitsOfMeasure.getUnitURI());
+    }
+
+    /**
+     * Distance (Great Circle) in the Units of Measure stated in URI.
+     *
+     * @param targetGeometry
+     * @param targetDistanceUnitsURI
+     * @return Distance
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     * @throws UnitsConversionException
+     */
+    public double distanceGreatCircle(GeometryWrapper targetGeometry, String targetDistanceUnitsURI) throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
+
+        GeometryWrapper transformedSourceGeometry;
+
+        //Check the conversion of Great Circle distance from metres into the requested units.
+        if (!UnitsRegistry.isLinearUnits(targetDistanceUnitsURI)) {
+            throw new UnitsConversionException("Great Circle distance units are metres and only linear conversion supported.");
+        }
+
+        if (srsInfo.isGeographic()) {
+            //Already a geographic SRS.
+            transformedSourceGeometry = this;
+        } else {
+            //Use WGS84 and not CRS84 as assuming WGS84 is more prevalent.
+            if (!GeoSPARQLConfig.ALLOW_GEOMETRY_SRS_TRANSFORMATION) {
+                throw new TransformException("GeometryLiteral SRS transformation is disabled, see GeoSPARQLConfig.allowGeometrySRSTransformation(...).");
+            }
+            transformedSourceGeometry = this.transform(SRS_URI.WGS84_CRS);
+        }
+
+        GeometryWrapper transformedTargetGeometry = transformedSourceGeometry.checkTransformSRS(targetGeometry);
+
+        //Find the nearest pair of coordinates from each Geometry using Euclidean distance (adjusting for wrap around).
+        //These are then used for Great Circle distance.
+        CoordinatePair coordinatePair = CoordinatePair.findNearestPair(transformedSourceGeometry, transformedTargetGeometry);
+
+        //Check whether the nearest pair are the same, i.e. they overlap or are within each other.
+        if (coordinatePair.isEqual()) {
+            //Exit early as the distance is zero.
+            return 0.0;
+        }
+
+        Coordinate coord1 = coordinatePair.getCoord1();
+        Coordinate coord2 = coordinatePair.getCoord2();
+
+        double distance = GreatCircleDistance.haversineFormula(coord1.getY(), coord1.getX(), coord2.getY(), coord2.getX());
+
+        //Convert the Great Circle distance from metres into the requested units.
+        return UnitsOfMeasure.conversion(distance, Unit_URI.METRE_URL, targetDistanceUnitsURI);
+    }
+
+    /**
+     * Distance (Euclidean or Great Circle depending on Geometry SRS URI)
+     * defaulting to metres.
+     *
+     * @param targetGeometry
+     * @return Distance
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     * @throws UnitsConversionException
+     */
+    public double distance(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
+        return distance(targetGeometry, Unit_URI.METRE_URL);
+    }
+
+    /**
+     * Distance (Euclidean or Great Circle depending on Geometry SRS URI) in the
+     * Units of Measure.
+     *
+     * @param targetGeometry
+     * @param unitsOfMeasure
+     * @return Distance
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     * @throws UnitsConversionException
+     */
+    public double distance(GeometryWrapper targetGeometry, UnitsOfMeasure unitsOfMeasure) throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
+        return distance(targetGeometry, unitsOfMeasure.getUnitURI());
+    }
+
+    /**
+     * Distance (Euclidean or Great Circle depending on Geometry SRS URI) in the
+     * Units of Measure stated in URI.
+     *
+     * @param targetGeometry
+     * @param targetDistanceUnitsURI
+     * @return Distance
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     * @throws UnitsConversionException
+     */
+    public double distance(GeometryWrapper targetGeometry, String targetDistanceUnitsURI) throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
+
+        double targetDistance;
+        if (srsInfo.isGeographic()) {
+            targetDistance = distanceGreatCircle(targetGeometry, targetDistanceUnitsURI);
+        } else {
+            targetDistance = distanceEuclidean(targetGeometry, targetDistanceUnitsURI);
+        }
+
+        return targetDistance;
+    }
+
+    /**
+     *
+     * @return Boundary of GeometryWrapper
+     */
+    public GeometryWrapper boundary() {
+        Geometry xyGeo = this.xyGeometry.getBoundary();
+        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
+        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo);
+    }
+
+    /**
+     *
+     * @return Convex Hull of GeometryWrapper
+     */
+    public GeometryWrapper convexHull() {
+        Geometry xyGeo = this.xyGeometry.convexHull();
+        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
+        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return Difference of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public GeometryWrapper difference(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        Geometry xyGeo = this.xyGeometry.difference(transformedGeometry.xyGeometry);
+        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
+        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo);
+    }
+
+    /**
+     * Envelope of GeometryWrapper with original coordinate order.
+     *
+     * @return Envelope of GeometryWrapper
+     */
+    public GeometryWrapper envelope() {
+        GeometryFactory geometryFactory = this.xyGeometry.getFactory();
+        Envelope xyEnvelope = this.getEnvelope();
+        Geometry xyGeo = geometryFactory.toGeometry(xyEnvelope);
+        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
+        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo);
+    }
+
+    /**
+     * Envelope of GeometryWrapper in XY order.
+     *
+     * @return Envelope of GeometryWrapper
+     */
+    public Envelope getEnvelope() {
+        if (envelope == null) {
+            envelope = this.xyGeometry.getEnvelopeInternal();
+        }
+
+        return envelope;
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return Intersection of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public GeometryWrapper intersection(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        Geometry xyGeo = this.xyGeometry.intersection(transformedGeometry.xyGeometry);
+        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
+        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return Intersection Matrix of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public IntersectionMatrix relate(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return xyGeometry.relate(transformedGeometry.xyGeometry);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @param intersectionPattern
+     * @return Relation of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public boolean relate(GeometryWrapper targetGeometry, String intersectionPattern) throws FactoryException, MismatchedDimensionException, TransformException {
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return xyGeometry.relate(transformedGeometry.xyGeometry, intersectionPattern);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return Symmetric Difference of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public GeometryWrapper symDifference(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        Geometry xyGeo = this.xyGeometry.symDifference(transformedGeometry.xyGeometry);
+        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
+        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo, null);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return Union of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public GeometryWrapper union(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        Geometry xyGeo = this.xyGeometry.union(transformedGeometry.xyGeometry);
+        Geometry parsingGeo = GeometryReverse.check(xyGeo, srsInfo);
+        return new GeometryWrapper(parsingGeo, xyGeo, srsInfo.getSrsURI(), geometryDatatypeURI, dimensionInfo, null);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return sfContains of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public boolean contains(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        this.checkPreparedGeometry();
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return this.preparedGeometry.contains(transformedGeometry.xyGeometry);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return sfCrosses of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public boolean crosses(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        this.checkPreparedGeometry();
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return this.preparedGeometry.crosses(transformedGeometry.xyGeometry);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return sfDisjoint of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public boolean disjoint(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        this.checkPreparedGeometry();
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return this.preparedGeometry.disjoint(transformedGeometry.xyGeometry);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return Topology equals of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public boolean equalsTopo(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return this.xyGeometry.equalsTopo(transformedGeometry.xyGeometry);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return Equals exactly of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public boolean equalsExact(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return this.xyGeometry.equalsExact(transformedGeometry.xyGeometry);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @param tolerance
+     * @return Equals exactly of GeometryWrapper with target using provided
+     * tolerance.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public boolean equalsExact(GeometryWrapper targetGeometry, double tolerance) throws FactoryException, MismatchedDimensionException, TransformException {
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return this.xyGeometry.equalsExact(transformedGeometry.xyGeometry, tolerance);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return sfIntersects of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public boolean intersects(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        this.checkPreparedGeometry();
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return this.preparedGeometry.intersects(transformedGeometry.xyGeometry);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return sfOverlaps of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public boolean overlaps(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        this.checkPreparedGeometry();
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return this.preparedGeometry.overlaps(transformedGeometry.xyGeometry);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return sfTouches of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public boolean touches(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        this.checkPreparedGeometry();
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return this.preparedGeometry.touches(transformedGeometry.xyGeometry);
+    }
+
+    /**
+     *
+     * @param targetGeometry
+     * @return sfWithin of GeometryWrapper with target.
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    public boolean within(GeometryWrapper targetGeometry) throws FactoryException, MismatchedDimensionException, TransformException {
+        this.checkPreparedGeometry();
+        GeometryWrapper transformedGeometry = checkTransformSRS(targetGeometry);
+        return this.preparedGeometry.within(transformedGeometry.xyGeometry);
+    }
+
+    /**
+     *
+     * @return GeometryWrapper as NodeValue
+     */
+    public NodeValue asNodeValue() throws DatatypeFormatException {
+        Literal literal = asLiteral();
+        return NodeValue.makeNode(literal.getLexicalForm(), literal.getDatatype());
+    }
+
+    /**
+     *
+     * @return GeometryWrapper as Node
+     */
+    public Node asNode() throws DatatypeFormatException {
+        return asNodeValue().asNode();
+    }
+
+    /**
+     *
+     * @return GeometryWrapper as Literal
+     */
+    public Literal asLiteral() throws DatatypeFormatException {
+
+        GeometryDatatype datatype = getGeometryDatatype(); //Datatype is only retrieved when required.
+        if (lexicalForm != null) {
+            return ResourceFactory.createTypedLiteral(lexicalForm, datatype);
+        }
+
+        Literal literal = asLiteral(datatype);
+        lexicalForm = literal.getLexicalForm();
+        return literal;
+    }
+
+    /**
+     *
+     * @param outputGeometryDatatypeURI
+     * @return GeometryWrapper as Literal in datatype form.
+     */
+    public Literal asLiteral(String outputGeometryDatatypeURI) throws DatatypeFormatException {
+        GeometryDatatype datatype = GeometryDatatype.get(outputGeometryDatatypeURI);
+        return asLiteral(datatype);
+    }
+
+    /**
+     *
+     * @param datatype
+     * @return GeometryWrapper as Literal
+     */
+    public Literal asLiteral(GeometryDatatype datatype) {
+        String tempLexicalForm = datatype.unparse(this);
+        return ResourceFactory.createTypedLiteral(tempLexicalForm, datatype);
+    }
+
+    /**
+     *
+     * @return Coordinate dimension, i.e. 2 (x,y), 3 (x,y,z or x,y,m) or 4
+     * (x,y,z,m)
+     */
+    public int getCoordinateDimension() {
+        return dimensionInfo.getCoordinate();
+    }
+
+    /**
+     *
+     * @return Spatial dimension, i.e. 2 or 3
+     */
+    public int getSpatialDimension() {
+        return dimensionInfo.getSpatial();
+    }
+
+    /**
+     *
+     * @return Topological dimension, i.e. 0, 1 or 2
+     */
+    public int getTopologicalDimension() {
+        return dimensionInfo.getTopological();
+    }
+
+    /**
+     *
+     * @return Enum of coordinate dimensions.
+     */
+    public CoordinateSequenceDimensions getCoordinateSequenceDimensions() {
+        return dimensionInfo.getDimensions();
+    }
+
+    /**
+     *
+     * @return Units of Measure for the GeometryWrapper SRS.
+     */
+    public UnitsOfMeasure getUnitsOfMeasure() {
+        return srsInfo.getUnitsOfMeasure();
+    }
+
+    /**
+     *
+     * @return GeometryWrapper's coordinate, spatial and topological dimensions.
+     */
+    public DimensionInfo getDimensionInfo() {
+        return dimensionInfo;
+    }
+
+    /**
+     *
+     * @return String literal of Geometry Wrapper.
+     */
+    public String getLexicalForm() {
+
+        if (lexicalForm != null) {
+            return lexicalForm;
+        } else {
+            Literal literal = asLiteral();
+            return literal.getLexicalForm();
+        }
+    }
+
+    /**
+     *
+     * @return Geometry is empty of coordinates.
+     */
+    public boolean isEmpty() {
+        return this.xyGeometry.isEmpty();
+    }
+
+    /**
+     *
+     * @return Geometry is in simple form.
+     */
+    public boolean isSimple() {
+        return this.xyGeometry.isSimple();
+    }
+
+    /**
+     *
+     * @return Geometry is topologically valid.
+     */
+    public boolean isValid() {
+        return this.xyGeometry.isValid();
+    }
+
+    /**
+     * Extract Geometry Wrapper from Geometry Literal.
+     *
+     * @param geometryLiteral
+     * @param targetIndex
+     * @return Geometry Wrapper of the Geometry Literal.
+     */
+    public static final GeometryWrapper extract(NodeValue geometryLiteral, GeometryIndex targetIndex) {
+
+        Node node = geometryLiteral.asNode();
+
+        return extract(node, targetIndex);
+    }
+
+    /**
+     * Extract Geometry Wrapper from Geometry Literal.
+     *
+     * @param geometryLiteral
+     * @param targetIndex
+     * @return Geometry Wrapper of the Geometry Literal.
+     */
+    public static final GeometryWrapper extract(Node geometryLiteral, GeometryIndex targetIndex) throws DatatypeFormatException {
+
+        if (!geometryLiteral.isLiteral()) {
+            throw new DatatypeFormatException("Not a Literal: " + geometryLiteral);
+        }
+
+        String datatypeURI = geometryLiteral.getLiteralDatatypeURI();
+        String lexicalForm = geometryLiteral.getLiteralLexicalForm();
+        return extract(lexicalForm, datatypeURI, targetIndex);
+    }
+
+    /**
+     * Extract Geometry Wrapper from Geometry Literal.
+     *
+     * @param geometryLiteral
+     * @return Geometry Wrapper of the Geometry Literal.
+     */
+    public static final GeometryWrapper extract(NodeValue geometryLiteral) {
+        return extract(geometryLiteral, GeometryIndex.PRIMARY);
+    }
+
+    /**
+     * Extract Geometry Wrapper from Geometry Literal.
+     *
+     * @param geometryLiteral
+     * @return Geometry Wrapper of the Geometry Literal.
+     */
+    public static final GeometryWrapper extract(Node geometryLiteral) {
+        return extract(geometryLiteral, GeometryIndex.PRIMARY);
+    }
+
+    /**
+     * Extract Geometry Wrapper from Geometry Literal. Returns null if invalid
+     * literal provided.
+     *
+     * @param geometryLiteral
+     * @param targetIndex
+     * @return Geometry Wrapper of the Geometry Literal.
+     */
+    public static final GeometryWrapper extract(Literal geometryLiteral, GeometryIndex targetIndex) {
+        return extract(geometryLiteral.getLexicalForm(), geometryLiteral.getDatatypeURI(), targetIndex);
+    }
+
+    /**
+     * Extract Geometry Wrapper from Geometry Literal.
+     *
+     * @param geometryLiteral
+     * @return Geometry Wrapper of the Geometry Literal.
+     */
+    public static final GeometryWrapper extract(Literal geometryLiteral) {
+        return extract(geometryLiteral, GeometryIndex.PRIMARY);
+    }
+
+    /**
+     * Extract Geometry Wrapper from Geometry Literal.
+     *
+     * @param lexicalForm
+     * @param datatypeURI
+     * @return Geometry Wrapper of the Geometry Literal.
+     */
+    public static GeometryWrapper extract(String lexicalForm, String datatypeURI) {
+        return extract(lexicalForm, datatypeURI, GeometryIndex.PRIMARY);
+    }
+
+    /**
+     * Extract Geometry Wrapper from Geometry Literal.
+     *
+     * @param lexicalForm
+     * @param datatypeURI
+     * @param targetIndex
+     * @return Geometry Wrapper of the Geometry Literal.
+     */
+    public static GeometryWrapper extract(String lexicalForm, String datatypeURI, GeometryIndex targetIndex) throws DatatypeFormatException {
+
+        if (lexicalForm == null || datatypeURI == null) {
+            throw new DatatypeFormatException("GeometryWrapper extraction: arguments cannot be null - " + lexicalForm + ", " + datatypeURI);
+        }
+
+        GeometryDatatype datatype = GeometryDatatype.get(datatypeURI);
+        GeometryWrapper geometry = datatype.parse(lexicalForm, targetIndex);
+        return geometry;
+    }
+
+    /**
+     * Builds a WKT Point of Geometry Wrapper.<br>
+     * This method does not use the GeometryLiteralIndex and so is best used for
+     * one of Geometry Wrappers.
+     *
+     * @param x
+     * @param y
+     * @param srsURI
+     *
+     * @return Geometry Wrapper of WKT Point.
+     */
+    public static final GeometryWrapper fromPoint(double x, double y, String srsURI) {
+        CustomCoordinateSequence coordSequence = CustomCoordinateSequence.createPoint(x, y);
+        GeometryWrapper geometryWrapper = new GeometryWrapper(coordSequence, WKTDatatype.URI, srsURI);
+        return geometryWrapper;
+    }
+
+    /**
+     *
+     * @return Empty GeometryWrapper in WKT datatype.
+     */
+    public static final GeometryWrapper getEmptyWKT() {
+        return WKTDatatype.INSTANCE.read("");
+    }
+
+    /**
+     *
+     * @return Empty GeometryWrapper in GML datatype.
+     */
+    public static final GeometryWrapper getEmptyGML() {
+        return GMLDatatype.INSTANCE.read("");
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 3;
+        hash = 23 * hash + Objects.hashCode(this.dimensionInfo);
+        hash = 23 * hash + Objects.hashCode(this.srsInfo);
+        hash = 23 * hash + Objects.hashCode(this.xyGeometry);
+        hash = 23 * hash + Objects.hashCode(this.geometryDatatypeURI);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final GeometryWrapper other = (GeometryWrapper) obj;
+        if (!Objects.equals(this.geometryDatatypeURI, other.geometryDatatypeURI)) {
+            return false;
+        }
+        if (!Objects.equals(this.dimensionInfo, other.dimensionInfo)) {
+            return false;
+        }
+        if (!Objects.equals(this.srsInfo, other.srsInfo)) {
+            return false;
+        }
+        return Objects.equals(this.xyGeometry, other.xyGeometry);
+    }
+
+    @Override
+    public String toString() {
+        return "GeometryWrapper{" + "dimensionInfo=" + dimensionInfo + ", geometryDatatypeURI=" + geometryDatatypeURI + ", lexicalForm=" + lexicalForm + ", utmURI=" + utmURI + ", latitude=" + latitude + ", xyGeometry=" + xyGeometry + ", parsingGeometry=" + parsingGeometry + ", preparedGeometry=" + preparedGeometry + ", srsInfo=" + srsInfo + '}';
+    }
+
+}
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/UnitsOfMeasure.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/UnitsOfMeasure.java
index 994ec9c..fab5643 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/UnitsOfMeasure.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/UnitsOfMeasure.java
@@ -104,7 +104,7 @@ public class UnitsOfMeasure implements Serializable {
         Boolean isTargetUnitsLinear = targetUnits.isLinearUnits();
 
         if (!isSourceUnitsLinear.equals(isTargetUnitsLinear)) {
-            throw new UnitsConversionException("Conversion between linear and non-linear units not supported (convertBetween method): " + sourceUnits.unitURI + " and " + targetUnits.unitURI);
+            throw new UnitsConversionException("Conversion between linear and non-linear units not supported: " + sourceUnits.unitURI + " and " + targetUnits.unitURI);
         }
 
         //Source and Target are the same units, so return the source distance.
@@ -124,30 +124,6 @@ public class UnitsOfMeasure implements Serializable {
     }
 
     /**
-     * Convert between linear and non-linear units and vice versa.<br>
-     * Will convert linear/linear and non-linear/non-linear units.
-     *
-     * @param distance
-     * @param unitsURI
-     * @param targetDistanceUnitsURI
-     * @param isTargetUnitsLinear
-     * @param latitude
-     * @return Distance in target units.
-     */
-    public static final double convertBetween(double distance, String unitsURI, String targetDistanceUnitsURI, boolean isTargetUnitsLinear, double latitude) {
-        double targetDistance;
-        if (isTargetUnitsLinear) {
-            double metresDistance = UnitsOfMeasure.convertToMetres(distance, unitsURI, latitude);
-            targetDistance = UnitsOfMeasure.conversion(metresDistance, Unit_URI.METRE_URL, targetDistanceUnitsURI);
-        } else {
-            double degreesDistance = UnitsOfMeasure.convertToDegrees(distance, unitsURI, latitude);
-            targetDistance = UnitsOfMeasure.conversion(degreesDistance, Unit_URI.DEGREE_URL, targetDistanceUnitsURI);
-        }
-
-        return targetDistance;
-    }
-
-    /**
      * Provides conversion of linear units to degrees.
      * <br> Conversion from linear (i.e. metres) to degrees based on equatorial
      * radius of 111.32km.
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/SpatialIndex.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/SpatialIndex.java
index 980aa01..952ca1f 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/SpatialIndex.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/SpatialIndex.java
@@ -29,6 +29,7 @@ import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import org.apache.commons.io.FileUtils;
 import org.apache.jena.geosparql.configuration.GeoSPARQLOperations;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
 import org.apache.jena.geosparql.implementation.SRSInfo;
@@ -458,7 +459,7 @@ public class SpatialIndex {
 
             Literal lat = feature.getRequiredProperty(SpatialExtension.GEO_LAT_PROP).getLiteral();
             Literal lon = feature.getProperty(SpatialExtension.GEO_LON_PROP).getLiteral();
-            if ( lon == null ) {
+            if (lon == null) {
                 LOGGER.warn("Geo predicates: latitude found but not longitude. " + feature);
                 continue;
             }
@@ -533,11 +534,18 @@ public class SpatialIndex {
         if (spatialIndexFile != null) {
             LOGGER.info("Saving Spatial Index - Started: {}", spatialIndexFile.getAbsolutePath());
             SpatialIndexStorage storage = new SpatialIndexStorage(spatialIndexItems, srsURI);
-            try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(spatialIndexFile))) {
-                out.writeObject(storage);
-                LOGGER.info("Saving Spatial Index - Completed: {}", spatialIndexFile.getAbsolutePath());
-            } catch (Exception ex) {
+            File file;
+            try {
+                file = File.createTempFile("spatial_index", null);
+                file.deleteOnExit();
+                try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file))) {
+                    out.writeObject(storage);
+                    FileUtils.copyFile(file, spatialIndexFile);
+                }
+            } catch (IOException ex) {
                 throw new SpatialIndexException("Save Exception: " + ex.getMessage());
+            } finally {
+                LOGGER.info("Saving Spatial Index - Completed: {}", spatialIndexFile.getAbsolutePath());
             }
         }
     }
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/DistanceFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/DistanceFF.java
index 0147d15..c6c355c 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/DistanceFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/DistanceFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.spatial.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.geosparql.implementation.index.GeometryLiteralIndex;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
@@ -53,7 +54,7 @@ public class DistanceFF extends FunctionBase3 {
             }
             double distance = geometry1.distance(geometry2, unitsURI);
             return NodeValue.makeDouble(distance);
-        } catch (DatatypeFormatException | FactoryException | MismatchedDimensionException | TransformException ex) {
+        } catch (DatatypeFormatException | FactoryException | MismatchedDimensionException | TransformException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
         }
     }
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleFF.java
index 8b9faa0..7f29679 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleFF.java
@@ -18,6 +18,7 @@
 package org.apache.jena.geosparql.spatial.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.geosparql.implementation.UnitsOfMeasure;
 import org.apache.jena.geosparql.implementation.great_circle.GreatCircleDistance;
 import org.apache.jena.geosparql.implementation.registry.UnitsRegistry;
@@ -71,11 +72,14 @@ public class GreatCircleFF extends FunctionBase5 {
             double distanceMetres = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2);
 
             //Convert the Great Circle distance from metres into the requested units.
-            Boolean isUnitsLinear = UnitsRegistry.isLinearUnits(unitsURI);
-            double distance = UnitsOfMeasure.convertBetween(distanceMetres, Unit_URI.METRE_URL, unitsURI, isUnitsLinear, lat1);
+            if (!UnitsRegistry.isLinearUnits(unitsURI)) {
+                throw new ExprEvalException("Great Circle distance units are metres and only linear conversion supported.");
+            }
+
+            double distance = UnitsOfMeasure.conversion(distanceMetres, Unit_URI.METRE_URL, unitsURI);
 
             return NodeValue.makeDouble(distance);
-        } catch (DatatypeFormatException ex) {
+        } catch (DatatypeFormatException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
         }
     }
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleGeomFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleGeomFF.java
index e80f0cc..d733d5d 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleGeomFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleGeomFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.spatial.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.geosparql.implementation.index.GeometryLiteralIndex;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
@@ -53,7 +54,7 @@ public class GreatCircleGeomFF extends FunctionBase3 {
             }
             double distance = geometry1.distanceGreatCircle(geometry2, unitsURI);
             return NodeValue.makeDouble(distance);
-        } catch (DatatypeFormatException | FactoryException | MismatchedDimensionException | TransformException ex) {
+        } catch (DatatypeFormatException | FactoryException | MismatchedDimensionException | TransformException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
         }
     }
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/NearbyFF.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/NearbyFF.java
index 4c031e8..33f01dc 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/NearbyFF.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/spatial/filter_functions/NearbyFF.java
@@ -19,6 +19,7 @@ package org.apache.jena.geosparql.spatial.filter_functions;
 
 import org.apache.jena.datatypes.DatatypeFormatException;
 import org.apache.jena.geosparql.implementation.GeometryWrapper;
+import org.apache.jena.geosparql.implementation.UnitsConversionException;
 import org.apache.jena.geosparql.implementation.index.GeometryLiteralIndex;
 import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
@@ -59,7 +60,7 @@ public class NearbyFF extends FunctionBase4 {
             }
             boolean result = relate(geometry1, geometry2, radius, unitsURI);
             return NodeValue.makeBoolean(result);
-        } catch (DatatypeFormatException ex) {
+        } catch (DatatypeFormatException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage(), ex);
         }
     }
@@ -68,7 +69,7 @@ public class NearbyFF extends FunctionBase4 {
         try {
             double distance = geometry1.distance(geometry2, unitsURI);
             return distance < radius;
-        } catch (FactoryException | MismatchedDimensionException | TransformException ex) {
+        } catch (FactoryException | MismatchedDimensionException | TransformException | UnitsConversionException ex) {
             throw new ExprEvalException(ex.getMessage() + ": " + geometry1.asLiteral() + ", " + geometry2.asLiteral() + ", " + Double.toString(radius) + ", " + unitsURI, ex);
         }
     }
diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BufferFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BufferFFTest.java
index 241f698..b7ebfb5 100644
--- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BufferFFTest.java
+++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/BufferFFTest.java
@@ -17,22 +17,17 @@
  */
 package org.apache.jena.geosparql.geof.nontopological.filter_functions;
 
-import org.apache.jena.geosparql.implementation.GeometryWrapper;
 import org.apache.jena.geosparql.implementation.datatype.WKTDatatype;
-import org.apache.jena.geosparql.implementation.vocabulary.SRS_URI;
 import org.apache.jena.geosparql.implementation.vocabulary.Unit_URI;
 import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
 import org.junit.After;
 import org.junit.AfterClass;
-import org.junit.Assert;
 import static org.junit.Assert.assertEquals;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
-import org.opengis.geometry.MismatchedDimensionException;
-import org.opengis.referencing.operation.TransformException;
-import org.opengis.util.FactoryException;
 
 /**
  *
@@ -77,86 +72,27 @@ public class BufferFFTest {
     /**
      * Test of exec method, of class BufferFF.
      */
-    @Test
-    public void testExec_Projection_NonLinear() {
+    @Test(expected = ExprEvalException.class)
+    public void testExec_Projection_NonLinear_exception() {
 
         NodeValue v1 = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/27700> POINT(60 60)", WKTDatatype.INSTANCE);
         NodeValue v2 = NodeValue.makeDecimal(0.0002);
         NodeValue v3 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.DEGREE_URL));
         BufferFF instance = new BufferFF();
-        NodeValue expResult = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/27700> POLYGON((74.40146 58.902956, 73.79184 54.591588, 72.686764 50.428725, 71.039028 46.753001, 68.992545 43.553727, 66.627526 40.936656, 64.024179 39.007539, 61.199008 37.98857, 58.359636 37.752618, 55.586275 38.405436, 52.942632 39.830584, 50.652834 42.123122, 48.700379 45.06086, 47.148974 48.527356, 46.062326 52.406168, 45.584349 56.686608, 45.61833 61.040732, 46.227974 65.352098, 47.33307 69.51 [...]
         NodeValue result = instance.exec(v1, v2, v3);
-        assertEquals(expResult, result);
     }
 
     /**
      * Test of exec method, of class BufferFF.
      */
-    @Test
-    public void testExec_Geographic_Linear() {
+    @Test(expected = ExprEvalException.class)
+    public void testExec_Geographic_Linear_Exception() {
 
         NodeValue v1 = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/4326> POLYGON((49.9 -7.5, 50.0 -7.5, 50.0 -7.4, 49.9 -7.4, 49.9 -7.5))", WKTDatatype.INSTANCE);
         NodeValue v2 = NodeValue.makeDecimal(20);
         NodeValue v3 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.METRE_URL));
         BufferFF instance = new BufferFF();
-        NodeValue expResult = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/4326> POLYGON((49.89982 -7.5, 49.899824 -7.500054, 49.899834 -7.500106, 49.89985 -7.500155, 49.899873 -7.500197, 49.8999 -7.500231, 49.899931 -7.500257, 49.899965 -7.500273, 49.9 -7.500278, 50 -7.500279, 50.000035 -7.500274, 50.000069 -7.500258, 50.0001 -7.500232, 50.000127 -7.500197, 50.00015 -7.500155, 50.000166 -7.500107, 50.000176 -7.500055, 50.00018 -7.5, 50.00018 -7.4, 50.000176 -7.399945, 50.0 [...]
         NodeValue result = instance.exec(v1, v2, v3);
-        assertEquals(expResult, result);
-    }
-
-    /**
-     * Test of exec method, of class BufferFF.
-     *
-     * @throws org.opengis.referencing.operation.TransformException
-     * @throws org.opengis.util.FactoryException
-     */
-    @Test
-    public void testExec_Geographic_Linear2() throws MismatchedDimensionException, TransformException, FactoryException {
-
-        //Test that buffering with Geographic geometry and Linear distance yields similar results to Projected geometry and Linear distance.
-        GeometryWrapper originalGeometryWrapper = GeometryWrapper.extract("<http://www.opengis.net/def/crs/EPSG/0/27700> POLYGON((0.0 0.0, 0.0 100.0, 100.0 100.0, 100.0 0.0, 0.0 0.0))", WKTDatatype.URI);
-
-        //Convert a projected GeometryWrapper to Geographic. Apply linear buffering. Convert back again.
-        NodeValue v1a = originalGeometryWrapper.transform(SRS_URI.WGS84_CRS).asNodeValue();
-        NodeValue v2 = NodeValue.makeDecimal(20);
-        NodeValue v3 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.METRE_URL));
-        BufferFF instance = new BufferFF();
-        NodeValue bufferedGeographicNodeValue = instance.exec(v1a, v2, v3);
-        GeometryWrapper bufferedGeographic = GeometryWrapper.extract(bufferedGeographicNodeValue);
-        String resultLexicalForm = bufferedGeographic.transform(SRS_URI.OSGB36_CRS).asLiteral().getLexicalForm();
-
-        //Apply linear buffering to projected GeometryWrapper.
-        NodeValue v1b = originalGeometryWrapper.asNodeValue();
-        NodeValue bufferedProjectedNodeValue = instance.exec(v1b, v2, v3);
-        GeometryWrapper bufferedProjected = GeometryWrapper.extract(bufferedProjectedNodeValue);
-        String expResultLexicalForm = bufferedProjected.asLiteral().getLexicalForm();
-
-        //Unpack the values into arrays so can do comparison with tolerance.
-        String[] resultsStr = resultLexicalForm.substring(resultLexicalForm.indexOf("((") + 2, resultLexicalForm.indexOf("))")).split(", ");
-        String[] expResultsStr = expResultLexicalForm.substring(expResultLexicalForm.indexOf("((") + 2, expResultLexicalForm.indexOf("))")).split(", ");
-
-        double[] results = new double[resultsStr.length * 2];
-        int i = 0;
-        for (String result : resultsStr) {
-            String[] res = result.split(" ");
-            results[i] = Double.parseDouble(res[0]);
-            i++;
-            results[i] = Double.parseDouble(res[1]);
-            i++;
-        }
-
-        double[] expResults = new double[expResultsStr.length * 2];
-        int j = 0;
-        for (String result : expResultsStr) {
-            String[] res = result.split(" ");
-            expResults[j] = Double.parseDouble(res[0]);
-            j++;
-            expResults[j] = Double.parseDouble(res[1]);
-            j++;
-        }
-
-        //Test accuracy of buffering to within 0.12m. Some error from coordinate and unit transformations.
-        Assert.assertArrayEquals(expResults, results, 0.12);
     }
 
     /**
diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DistanceFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DistanceFFTest.java
index 5f0a890..a6ba7f9 100644
--- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DistanceFFTest.java
+++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/geof/nontopological/filter_functions/DistanceFFTest.java
@@ -17,9 +17,11 @@
  */
 package org.apache.jena.geosparql.geof.nontopological.filter_functions;
 
+import org.apache.jena.geosparql.configuration.GeoSPARQLConfig;
 import org.apache.jena.geosparql.implementation.datatype.WKTDatatype;
 import org.apache.jena.geosparql.implementation.vocabulary.Unit_URI;
 import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.sparql.expr.ExprEvalException;
 import org.apache.jena.sparql.expr.NodeValue;
 import org.junit.After;
 import org.junit.AfterClass;
@@ -57,7 +59,7 @@ public class DistanceFFTest {
      * Test of exec method, of class DistanceFF.
      */
     @Test
-    public void testExec_metres() {
+    public void testExec_projected_metres() {
 
         NodeValue v1 = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/27700> POINT(60 60)", WKTDatatype.INSTANCE);
         NodeValue v2 = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/27700> POINT(90 60)", WKTDatatype.INSTANCE);
@@ -71,16 +73,81 @@ public class DistanceFFTest {
     /**
      * Test of exec method, of class DistanceFF.
      */
-    @Test
-    public void testExec_radians() {
+    @Test(expected = ExprEvalException.class)
+    public void testExec_projected_radians_exception() {
 
         NodeValue v1 = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/27700> POINT(60 60)", WKTDatatype.INSTANCE);
         NodeValue v2 = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/27700> POINT(90 60)", WKTDatatype.INSTANCE);
         NodeValue v3 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.RADIAN_URL));
         DistanceFF instance = new DistanceFF();
-        double expResult = 7.2822E-6;
+        double result = instance.exec(v1, v2, v3).getDouble();
+    }
+
+    /**
+     * Test of exec method, of class DistanceFF.
+     */
+    @Test
+    public void testExec_geographic_radians() {
+
+        NodeValue v1 = NodeValue.makeNode("Point(11.41 53.63)", WKTDatatype.INSTANCE);
+        NodeValue v2 = NodeValue.makeNode("Point(11.57 48.13)", WKTDatatype.INSTANCE);
+        NodeValue v3 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.RADIAN_URL));
+        DistanceFF instance = new DistanceFF();
+        double expResult = 0.096034;
         double result = instance.exec(v1, v2, v3).getDouble();
         assertEquals(expResult, result, 0.0001);
     }
 
+    /**
+     * Test of exec method, of class DistanceFF.
+     */
+    @Test(expected = ExprEvalException.class)
+    public void testExec_geographic_metres_exception() {
+
+        NodeValue v1 = NodeValue.makeNode("Point(11.41 53.63)", WKTDatatype.INSTANCE);
+        NodeValue v2 = NodeValue.makeNode("Point(11.57 48.13)", WKTDatatype.INSTANCE);
+        NodeValue v3 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.KILOMETRE_URN));
+        DistanceFF instance = new DistanceFF();
+        double result = instance.exec(v1, v2, v3).getDouble();
+    }
+
+    /**
+     * Test of exec method, of class DistanceFF.
+     */
+    @Test(expected = ExprEvalException.class)
+    public void testExec_conversion_exception() {
+
+        GeoSPARQLConfig.allowGeometrySRSTransformation(false);     // Disable default config.
+        NodeValue v1 = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/27700> POINT(90 60)", WKTDatatype.INSTANCE);
+        NodeValue v2 = NodeValue.makeNode("Point(11.57 48.13)", WKTDatatype.INSTANCE);
+        NodeValue v3 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.KILOMETRE_URN));
+        DistanceFF instance = new DistanceFF();
+        try {
+            double aResult = instance.exec(v1, v2, v3).getDouble();
+        } catch (ExprEvalException ex) {
+            throw ex;
+        } finally {
+            GeoSPARQLConfig.allowGeometrySRSTransformation(true);
+        }
+    }
+
+    /**
+     * Test of exec method, of class DistanceFF.
+     */
+    @Test(expected = ExprEvalException.class)
+    public void testExec_conversion_exception_units() {
+        // Still receive an expection as the units of v1 and v3 don't align.
+        GeoSPARQLConfig.allowGeometrySRSTransformation(false);     // Disable default config.
+        NodeValue v1 = NodeValue.makeNode("Point(11.57 48.13)", WKTDatatype.INSTANCE);
+        NodeValue v2 = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/27700> POINT(90 60)", WKTDatatype.INSTANCE);
+        NodeValue v3 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.KILOMETRE_URN));
+        DistanceFF instance = new DistanceFF();
+        try {
+            double aResult = instance.exec(v1, v2, v3).getDouble();
+        } catch (ExprEvalException ex) {
+            throw ex;
+        } finally {
+            GeoSPARQLConfig.allowGeometrySRSTransformation(true);
+        }
+    }
 }
diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/GeometryWrapperTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/GeometryWrapperTest.java
index e753cf1..87e0430 100644
--- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/GeometryWrapperTest.java
+++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/GeometryWrapperTest.java
@@ -204,10 +204,11 @@ public class GeometryWrapperTest {
     /**
      * Test of distanceEuclidean same SRS_URI method, of class GeometryWrapper.
      *
-     * @throws java.lang.Exception
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
      */
     @Test
-    public void testDistanceSameSRSSameUnit() throws Exception {
+    public void testDistanceSameSRSSameUnit() throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
 
         Geometry targetGeo = GEOMETRY_FACTORY.createPoint(new Coordinate(2.0, 1.0));
         String targetSRSURI = SRS_URI.OSGB36_CRS;
@@ -228,10 +229,11 @@ public class GeometryWrapperTest {
     /**
      * Test of distanceEuclidean same SRS_URI method, of class GeometryWrapper.
      *
-     * @throws java.lang.Exception
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
      */
-    @Test
-    public void testDistanceSameSRSDifferentUnit() throws Exception {
+    @Test(expected = UnitsConversionException.class)
+    public void testDistanceSameSRSDifferentUnit_exception() throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
 
         Geometry targetGeo = GEOMETRY_FACTORY.createPoint(new Coordinate(385458, 156785)); //LatLon - 51.31, -2.21
         String targetSRSURI = SRS_URI.OSGB36_CRS;
@@ -244,19 +246,18 @@ public class GeometryWrapperTest {
         //SRS is in metres.
         String distanceUnitsURL = Unit_URI.RADIAN_URL;
 
-        double expResult = 0.025656; //Degree: 1.47
         double result = instance.distanceEuclidean(targetGeometry, distanceUnitsURL);
-        assertEquals(expResult, result, 0.001);
     }
 
     /**
      * Test of distanceEuclidean different SRS_URI method, of class
      * GeometryWrapper.
      *
-     * @throws java.lang.Exception
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
      */
     @Test
-    public void testDistanceDifferentSRSSameUnit() throws Exception {
+    public void testDistanceDifferentSRSSameUnit() throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
 
         Geometry targetGeo = GEOMETRY_FACTORY.createPoint(new Coordinate(2.0, 1.0));
         String targetSRSURI = SRS_URI.WGS84_CRS;
@@ -278,10 +279,11 @@ public class GeometryWrapperTest {
      * Test of distanceEuclidean different SRS_URI method, of class
      * GeometryWrapper.
      *
-     * @throws java.lang.Exception
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
      */
-    @Test
-    public void testDistanceDifferentSRSDifferentUnit() throws Exception {
+    @Test(expected = UnitsConversionException.class)
+    public void testDistanceDifferentSRSDifferentUnit_exception() throws FactoryException, MismatchedDimensionException, TransformException, UnitsConversionException {
 
         Geometry targetGeo = GEOMETRY_FACTORY.createPoint(new Coordinate(0.0, 1.0));
         String targetSRSURI = SRS_URI.WGS84_CRS;
@@ -294,9 +296,7 @@ public class GeometryWrapperTest {
         //SRS is in degrees.
         String distanceUnitsURL = Unit_URI.METRE_URL;
 
-        double expResult = 111320; //1.0 degree of longigtude at the equator is approx 111.32km.
         double result = instance.distanceEuclidean(targetGeometry, distanceUnitsURL);
-        assertEquals(expResult, result, 1);
     }
 
     /**
@@ -494,4 +494,34 @@ public class GeometryWrapperTest {
         assertEquals(expResult, result, 0.0001);
     }
 
+    /**
+     * Test of testGetUTMZoneURI_wgs84 method, of class GeometryWrapper.
+     *
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    @Test
+    public void testGetUTMZoneURI_wgs84() throws FactoryException, MismatchedDimensionException, TransformException {
+
+        GeometryWrapper instance = GeometryWrapper.extract("<http://www.opengis.net/def/crs/EPSG/0/4326> POINT(52.28 -1.58)", WKTDatatype.URI);      //Warwick Castle 
+        String expResult = "http://www.opengis.net/def/crs/EPSG/0/32630";
+        String result = instance.getUTMZoneURI();
+        assertEquals(expResult, result);
+    }
+    
+    /**
+     * Test of testGetUTMZoneURI_crs84 method, of class GeometryWrapper.
+     *
+     * @throws org.opengis.util.FactoryException
+     * @throws org.opengis.referencing.operation.TransformException
+     */
+    @Test
+    public void testGetUTMZoneURI_crs84() throws FactoryException, MismatchedDimensionException, TransformException {
+
+        GeometryWrapper instance = GeometryWrapper.extract("POINT(-1.58 52.28)", WKTDatatype.URI);      //Warwick Castle 
+        String expResult = "http://www.opengis.net/def/crs/EPSG/0/32630";
+        String result = instance.getUTMZoneURI();
+        assertEquals(expResult, result);
+    }
+    
 }
diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleFFTest.java
index ac207b7..654d73c 100644
--- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleFFTest.java
+++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleFFTest.java
@@ -130,9 +130,7 @@ public class GreatCircleFFTest {
         NodeValue v4 = NodeValue.makeDouble(20.0001);
         NodeValue v5 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.KILOMETER_URL));
         GreatCircleFF instance = new GreatCircleFF();
-        NodeValue expResult = NodeValue.makeDouble(20);
         NodeValue result = instance.exec(v1, v2, v3, v4, v5);
-        assertEquals(expResult, result);
     }
 
     /**
@@ -147,9 +145,7 @@ public class GreatCircleFFTest {
         NodeValue v4 = NodeValue.makeDouble(20.0001);
         NodeValue v5 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.KILOMETER_URL));
         GreatCircleFF instance = new GreatCircleFF();
-        NodeValue expResult = NodeValue.makeDouble(20);
         NodeValue result = instance.exec(v1, v2, v3, v4, v5);
-        assertEquals(expResult, result);
     }
 
     /**
@@ -164,9 +160,7 @@ public class GreatCircleFFTest {
         NodeValue v4 = NodeValue.makeDouble(20.0001);
         NodeValue v5 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.KILOMETER_URL));
         GreatCircleFF instance = new GreatCircleFF();
-        NodeValue expResult = NodeValue.makeDouble(20);
         NodeValue result = instance.exec(v1, v2, v3, v4, v5);
-        assertEquals(expResult, result);
     }
 
     /**
@@ -181,9 +175,7 @@ public class GreatCircleFFTest {
         NodeValue v4 = NodeValue.makeString("20.0001");
         NodeValue v5 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.KILOMETER_URL));
         GreatCircleFF instance = new GreatCircleFF();
-        NodeValue expResult = NodeValue.makeDouble(20);
         NodeValue result = instance.exec(v1, v2, v3, v4, v5);
-        assertEquals(expResult, result);
     }
 
     /**
@@ -198,9 +190,22 @@ public class GreatCircleFFTest {
         NodeValue v4 = NodeValue.makeDouble(20.0001);
         NodeValue v5 = NodeValue.makeInteger(20);
         GreatCircleFF instance = new GreatCircleFF();
-        NodeValue expResult = NodeValue.makeDouble(20);
         NodeValue result = instance.exec(v1, v2, v3, v4, v5);
-        assertEquals(expResult, result);
+    }
+
+    /**
+     * Test of exec method, of class GreatCircleFF.
+     */
+    @Test(expected = ExprEvalException.class)
+    public void testExec_units_exception() {
+
+        NodeValue v1 = NodeValue.makeDouble(10.0);
+        NodeValue v2 = NodeValue.makeDouble(20.0);
+        NodeValue v3 = NodeValue.makeDouble(10.0);
+        NodeValue v4 = NodeValue.makeDouble(20.0001);
+        NodeValue v5 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.RADIAN_URL));
+        GreatCircleFF instance = new GreatCircleFF();
+        NodeValue result = instance.exec(v1, v2, v3, v4, v5);
     }
 
     /**
@@ -230,8 +235,6 @@ public class GreatCircleFFTest {
 
         List<Literal> expResults = Arrays.asList(ResourceFactory.createTypedLiteral("344.266423e0", XSDDatatype.XSDdouble));
 
-        //
-        //
         assertEquals(expResults, results);
     }
 
diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleGeomFFTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleGeomFFTest.java
index 78df498..5a5fc82 100644
--- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleGeomFFTest.java
+++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/spatial/filter_functions/GreatCircleGeomFFTest.java
@@ -161,6 +161,19 @@ public class GreatCircleGeomFFTest {
     /**
      * Test of exec method, of class GreatCircleGeomFF.
      */
+    @Test(expected = ExprEvalException.class)
+    public void testExec_units_exception() {
+
+        NodeValue v1 = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/4326> POINT(10.0 20.0)", WKTDatatype.INSTANCE);
+        NodeValue v2 = NodeValue.makeNode("<http://www.opengis.net/def/crs/EPSG/0/4326> POINT(10.0 20.0001)", WKTDatatype.INSTANCE);
+        NodeValue v3 = NodeValue.makeNode(NodeFactory.createURI(Unit_URI.RADIAN_URL));
+        GreatCircleGeomFF instance = new GreatCircleGeomFF();
+        NodeValue result = instance.exec(v1, v2, v3);
+    }
+
+    /**
+     * Test of exec method, of class GreatCircleGeomFF.
+     */
     @Test
     public void testExec_query() {