You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by ga...@apache.org on 2020/06/14 16:14:37 UTC
[jena] branch master updated: Fixed JENA-1915 and added additional
test values.
This is an automated email from the ASF dual-hosted git repository.
galbiston pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jena.git
The following commit(s) were added to refs/heads/master by this push:
new bb90563 Fixed JENA-1915 and added additional test values.
new f7ec31c Merge pull request #756 from galbiston/great_circle_formula_fix
bb90563 is described below
commit bb90563d978b72c6ea378d22f51dc44bb7ea8f01
Author: Greg Albiston <46...@users.noreply.github.com>
AuthorDate: Sun Jun 14 17:07:38 2020 +0100
Fixed JENA-1915 and added additional test values.
---
.../geosparql/implementation/GeometryWrapper.java | 2498 ++++++++++----------
.../great_circle/GreatCircleDistance.java | 15 +-
.../spatial/filter_functions/GreatCircleFF.java | 2 +-
.../great_circle/GreatCircleDistanceTest.java | 339 ++-
4 files changed, 1597 insertions(+), 1257 deletions(-)
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 2e32f73..4de0749 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,1250 +1,1248 @@
-/*
- * 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();
-
- //Vincenty Formula is apparently more accurate at the Longitude boundary.
- double distance = GreatCircleDistance.vincentyFormula(coord1.getY(), coord1.getX(), coord2.getY(), coord2.getX());
- //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.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 + '}';
+ }
+
+}
diff --git a/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/great_circle/GreatCircleDistance.java b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/great_circle/GreatCircleDistance.java
index 3bedd79..a793489 100644
--- a/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/great_circle/GreatCircleDistance.java
+++ b/jena-geosparql/src/main/java/org/apache/jena/geosparql/implementation/great_circle/GreatCircleDistance.java
@@ -22,7 +22,10 @@ import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Point;
/**
- *
+ * Great Circle distance uses Haversine formula. There are situations where the
+ * Vincenty formula is more accurate. However, the implemented Vincenty formula
+ * has errors, see Test 'testVincentyFormula_Distance8' and
+ * 'testVincentyFormula_Distance8'.
*
*/
public class GreatCircleDistance {
@@ -100,12 +103,14 @@ public class GreatCircleDistance {
double b = Math.pow(Math.cos(lat1Rad) * Math.sin(lat2Rad) - Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(diffLonRad), 2);
double c = Math.sqrt(a + b);
- double d = Math.sin(lat1Rad) * Math.sin(lat2Rad) + Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.cos(diffLonRad);
+ double d = Math.abs(Math.sin(lat1Rad) * Math.sin(lat2Rad) + Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.cos(diffLonRad));
double e = Math.atan(c / d);
double distance = UnitsOfMeasure.EARTH_MEAN_RADIUS * e;
+ System.out.println("φ1: " + lat1Rad + ", φ2: " + lat2Rad + ", Δφ: " + diffLonRad + ", c: " + c + ", d: " + d + ", e: " + e);
+
//Distance is in metres.
return distance;
}
@@ -136,7 +141,7 @@ public class GreatCircleDistance {
* @return Distance in metres.
*/
public static final double haversineFormula(Point point1, Point point2) {
- //Based on Vincenty formula: https://en.wikipedia.org/wiki/Great-circle_distance
+ //Based on Haversine formula: https://en.wikipedia.org/wiki/Great-circle_distance
double lat1 = point1.getY();
double lon1 = point1.getX();
@@ -154,7 +159,7 @@ public class GreatCircleDistance {
* @return Distance in metres.
*/
public static final double haversineFormula(Coordinate coord1, Coordinate coord2) {
- //Based on Vincenty formula: https://en.wikipedia.org/wiki/Great-circle_distance
+ //Based on Haversine formula: https://en.wikipedia.org/wiki/Great-circle_distance
double lat1 = coord1.getY();
double lon1 = coord1.getX();
@@ -174,7 +179,7 @@ public class GreatCircleDistance {
*/
public static final double haversineFormula(double lat1, double lon1, double lat2, double lon2) {
//Based on Haversine formula: https://www.movable-type.co.uk/scripts/latlong.html
- //Apparently there are inaccurcies for distances of points on opposite sides of the sphere so prefer Vincenty formula.
+ //Apparently there are inaccurcies for distances of points on opposite sides of the sphere.
double lat1Rad = Math.toRadians(lat1);
double lat2Rad = Math.toRadians(lat2);
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 de163e6..8b9faa0 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
@@ -68,7 +68,7 @@ public class GreatCircleFF extends FunctionBase5 {
} else {
unitsURI = v5.asString();
}
- double distanceMetres = GreatCircleDistance.vincentyFormula(lat1, lon1, lat2, lon2);
+ double distanceMetres = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2);
//Convert the Great Circle distance from metres into the requested units.
Boolean isUnitsLinear = UnitsRegistry.isLinearUnits(unitsURI);
diff --git a/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/great_circle/GreatCircleDistanceTest.java b/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/great_circle/GreatCircleDistanceTest.java
index e44c20b..08eb69f 100644
--- a/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/great_circle/GreatCircleDistanceTest.java
+++ b/jena-geosparql/src/test/java/org/apache/jena/geosparql/implementation/great_circle/GreatCircleDistanceTest.java
@@ -22,6 +22,7 @@ import org.junit.AfterClass;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
/**
@@ -70,13 +71,349 @@ public class GreatCircleDistanceTest {
@Test
public void testVincentyFormula_Paris_London() {
+ double lat1 = 48.85341;
+ double lon1 = 2.34880;
double lat2 = 51.50853;
double lon2 = -0.12574;
+ double expResult = 343771;
+ double result = GreatCircleDistance.vincentyFormula(lat1, lon1, lat2, lon2);
+ assertEquals(expResult, result, 1);
+ }
+
+ /**
+ * Test of vincentyFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testVincentyFormula_Distance1() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 41.2592;
+ double lon2 = -95.9339;
+ double expResult = 2.8736;
+ double result = GreatCircleDistance.vincentyFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of vincentyFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testVincentyFormula_Distance2() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 41.2482;
+ double lon2 = -96.072;
+ double expResult = 9.1893;
+ double result = GreatCircleDistance.vincentyFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of vincentyFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testVincentyFormula_Distance3() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 41.5871;
+ double lon2 = -93.626;
+ double expResult = 198.3806;
+ double result = GreatCircleDistance.vincentyFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of vincentyFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testVincentyFormula_Distance4() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 51.0472;
+ double lon2 = -113.9998;
+ double expResult = 1757.1519;
+ double result = GreatCircleDistance.vincentyFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of vincentyFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testVincentyFormula_Distance5() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 40.7528;
+ double lon2 = -73.9876;
+ double expResult = 1840.1915;
+ double result = GreatCircleDistance.vincentyFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of vincentyFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testVincentyFormula_Distance6() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 49.7237;
+ double lon2 = 13.3422;
+ double expResult = 7781.1365;
+ double result = GreatCircleDistance.vincentyFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of vincentyFormula method, test values from issue JENA-1915.
+ */
+ @Ignore
+ @Test
+ public void testVincentyFormula_Distance7() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = -33.9065;
+ double lon2 = 18.4175;
+ double expResult = 14312.6630;
+ double result = GreatCircleDistance.vincentyFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of vincentyFormula method, test values from issue JENA-1915.
+ */
+ @Ignore
+ @Test
+ public void testVincentyFormula_Distance8() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = -33.8646;
+ double lon2 = 151.2099;
+ double expResult = 14184.1430;
+ double result = GreatCircleDistance.vincentyFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of haversineFormula method, of class GreatCircleDistance.
+ */
+ @Test
+ public void testHaversineFormula_London_Paris() {
+
+ double lat1 = 51.50853;
+ double lon1 = -0.12574;
+ double lat2 = 48.85341;
+ double lon2 = 2.34880;
+ double expResult = 343771;
+ double result = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2);
+ assertEquals(expResult, result, 1);
+ }
+
+ /**
+ * Test of haversineFormula method, of class GreatCircleDistance.
+ */
+ @Test
+ public void testHaversineFormula_Paris_London() {
+
double lat1 = 48.85341;
double lon1 = 2.34880;
+ double lat2 = 51.50853;
+ double lon2 = -0.12574;
double expResult = 343771;
- double result = GreatCircleDistance.vincentyFormula(lat1, lon1, lat2, lon2);
+ double result = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2);
assertEquals(expResult, result, 1);
}
+ /**
+ * Test of haversineFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testHaversineFormula_Distance1() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 41.2592;
+ double lon2 = -95.9339;
+ double expResult = 2.8736;
+ double result = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of haversineFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testHaversineFormula_Distance2() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 41.2482;
+ double lon2 = -96.072;
+ double expResult = 9.1893;
+ double result = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of haversineFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testHaversineFormula_Distance3() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 41.5871;
+ double lon2 = -93.626;
+ double expResult = 198.3806;
+ double result = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of haversineFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testHaversineFormula_Distance4() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 51.0472;
+ double lon2 = -113.9998;
+ double expResult = 1757.1519;
+ double result = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of haversineFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testHaversineFormula_Distance5() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 40.7528;
+ double lon2 = -73.9876;
+ double expResult = 1840.1915;
+ double result = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of haversineFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testHaversineFormula_Distance6() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = 49.7237;
+ double lon2 = 13.3422;
+ double expResult = 7781.1365;
+ double result = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of haversineFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testHaversineFormula_Distance7() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = -33.9065;
+ double lon2 = 18.4175;
+ double expResult = 14312.6630;
+ double result = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
+ /**
+ * Test of haversineFormula method, test values from issue JENA-1915.
+ */
+ @Test
+ public void testHaversineFormula_Distance8() {
+
+ double lat1 = 41.2572;
+ double lon1 = -95.9656;
+ double lat2 = -33.8646;
+ double lon2 = 151.2099;
+ double expResult = 14184.1430;
+ double result = GreatCircleDistance.haversineFormula(lat1, lon1, lat2, lon2) / 1000; // Adjustment to kilometres.
+
+ System.out.println("Exp: " + expResult);
+ System.out.println("Res: " + result);
+
+ assertEquals(expResult, result, 0.3);
+ }
+
}