diff --git a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/commands/expressions/DistanceFunctionTest.scala b/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/commands/expressions/DistanceFunctionTest.scala index b3a1f64d3470c..7e021ceb541b6 100644 --- a/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/commands/expressions/DistanceFunctionTest.scala +++ b/community/cypher/interpreted-runtime/src/test/scala/org/neo4j/cypher/internal/runtime/interpreted/commands/expressions/DistanceFunctionTest.scala @@ -20,7 +20,7 @@ package org.neo4j.cypher.internal.runtime.interpreted.commands.expressions import org.neo4j.cypher.internal.util.v3_4.test_helpers.CypherFunSuite -import org.neo4j.values.storable.{CoordinateReferenceSystem, PointValue, Values} +import org.neo4j.values.storable.{CRSCalculator, CoordinateReferenceSystem, PointValue, Values} import org.scalactic.{Equality, TolerantNumerics} import org.scalatest.matchers.{MatchResult, Matcher} @@ -116,7 +116,7 @@ class DistanceFunctionTest extends CypherFunSuite { val farWest = Values.pointValue(CoordinateReferenceSystem.WGS84, -179.99, 0) val farEast = Values.pointValue(CoordinateReferenceSystem.WGS84, 179.99, 0) - distance(farEast, farWest) should be < CoordinateReferenceSystem.GeographicCalculator.EARTH_RADIUS_METERS + distance(farEast, farWest) should be < CRSCalculator.GeographicCalculator.EARTH_RADIUS_METERS } test("bounding box including the north pole should be extended to all longitudes") { @@ -186,7 +186,7 @@ class DistanceFunctionTest extends CypherFunSuite { val lat1 = Math.toRadians(startingPoint.coordinate()(1)) val long1 = Math.toRadians(startingPoint.coordinate()(0)) - val R = CoordinateReferenceSystem.GeographicCalculator.EARTH_RADIUS_METERS + val R = CRSCalculator.GeographicCalculator.EARTH_RADIUS_METERS val lat2 = Math.asin(Math.sin(lat1) * Math.cos(d / R) + Math.cos(lat1) * Math.sin(d / R) * Math.cos(brng)) val long2 = long1 + Math.atan2(Math.sin(brng) * Math.sin(d / R) * Math.cos(lat1), Math.cos(d / R) - Math.sin(lat1) * Math.sin(lat2)) val normLong2 = (long2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI diff --git a/community/values/src/main/java/org/neo4j/values/storable/CRSCalculator.java b/community/values/src/main/java/org/neo4j/values/storable/CRSCalculator.java new file mode 100644 index 0000000000000..1fa27b9302e03 --- /dev/null +++ b/community/values/src/main/java/org/neo4j/values/storable/CRSCalculator.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.values.storable; + +import org.neo4j.helpers.collection.Pair; + +import static java.lang.Math.asin; +import static java.lang.Math.atan2; +import static java.lang.Math.cos; +import static java.lang.Math.pow; +import static java.lang.Math.sin; +import static java.lang.Math.sqrt; +import static java.lang.Math.toDegrees; +import static java.lang.Math.toRadians; + +public abstract class CRSCalculator +{ + public abstract double distance( PointValue p1, PointValue p2 ); + + public abstract Pair boundingBox( PointValue center, double distance ); + + protected static double pythagoras( double[] a, double[] b ) + { + double sqrSum = 0.0; + for ( int i = 0; i < a.length; i++ ) + { + double diff = a[i] - b[i]; + sqrSum += diff * diff; + } + return sqrt( sqrSum ); + } + + public static class CartesianCalculator extends CRSCalculator + { + int dimension; + + CartesianCalculator( int dimension ) + { + this.dimension = dimension; + } + + @Override + public double distance( PointValue p1, PointValue p2 ) + { + assert p1.getCoordinateReferenceSystem().getDimension() == dimension; + assert p2.getCoordinateReferenceSystem().getDimension() == dimension; + return pythagoras( p1.coordinate(), p2.coordinate() ); + } + + @Override + public Pair boundingBox( PointValue center, double distance ) + { + assert center.getCoordinateReferenceSystem().getDimension() == dimension; + double[] coordinates = center.coordinate(); + double[] min = new double[dimension]; + double[] max = new double[dimension]; + for ( int i = 0; i < dimension; i++ ) + { + min[i] = coordinates[i] - distance; + max[i] = coordinates[i] + distance; + } + CoordinateReferenceSystem crs = center.getCoordinateReferenceSystem(); + return Pair.of( Values.pointValue( crs, min ), Values.pointValue( crs, max ) ); + } + } + + public static class GeographicCalculator extends CRSCalculator + { + public static final double EARTH_RADIUS_METERS = 6378140.0; + private static final double EXTENSION_FACTOR = 1.0001; + int dimension; + + GeographicCalculator( int dimension ) + { + this.dimension = dimension; + } + + @Override + public double distance( PointValue p1, PointValue p2 ) + { + assert p1.getCoordinateReferenceSystem().getDimension() == dimension; + assert p2.getCoordinateReferenceSystem().getDimension() == dimension; + double[] c1Coord = p1.coordinate(); + double[] c2Coord = p2.coordinate(); + double[] c1 = new double[]{toRadians( c1Coord[0] ), toRadians( c1Coord[1] )}; + double[] c2 = new double[]{toRadians( c2Coord[0] ), toRadians( c2Coord[1] )}; + double dx = c2[0] - c1[0]; + double dy = c2[1] - c1[1]; + double alpha = pow( sin( dy / 2 ), 2.0 ) + cos( c1[1] ) * cos( c2[1] ) * pow( sin( dx / 2.0 ), 2.0 ); + double greatCircleDistance = 2.0 * atan2( sqrt( alpha ), sqrt( 1 - alpha ) ); + double distance2D = EARTH_RADIUS_METERS * greatCircleDistance; + if ( dimension > 2 ) + { + double[] a = new double[dimension - 1]; + double[] b = new double[dimension - 1]; + a[0] = distance2D; + b[0] = 0.0; + for ( int i = 1; i < dimension - 1; i++ ) + { + a[i] = 0.0; + b[i] = c1Coord[i + 1] - c2Coord[i + 1]; + } + return pythagoras( a, b ); + } + else + { + return distance2D; + } + } + + @Override + // http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates + // But calculating in degrees instead of radians to avoid rounding errors + public Pair boundingBox( PointValue center, double distance ) + { + if ( distance == 0.0 ) + { + return Pair.of( center, center ); + } + + // Extend the distance slightly to assure that all relevant points lies inside the bounding box, + // with rounding errors taken into account + double extended_distance = distance * EXTENSION_FACTOR; + + CoordinateReferenceSystem crs = center.getCoordinateReferenceSystem(); + double lat = center.coordinate()[1]; + double lon = center.coordinate()[0]; + + double r = extended_distance / EARTH_RADIUS_METERS; + + double lat_min = lat - toDegrees( r ); + double lat_max = lat + toDegrees( r ); + + // If your query circle includes one of the poles + if ( lat_max >= 90 ) + { + return boundingBoxOf( -180, 180, lat_min, 90, center, distance ); + } + else if ( lat_min <= -90 ) + { + return boundingBoxOf( -180, 180, -90, lat_max, center, distance ); + } + else + { + double delta_lon = toDegrees( asin( sin( r ) / cos( toRadians( lat ) ) ) ); + double lon_min = lon - delta_lon; + double lon_max = lon + delta_lon; + + // If you query circle wraps around the dateline + // Large rectangle covering all longitudes + // TODO implement two rectangle solution instead + if ( lon_min < -180 || lon_max > 180 ) + { + return boundingBoxOf( -180, 180, lat_min, lat_max, center, distance ); + } + else + { + return boundingBoxOf( lon_min, lon_max, lat_min, lat_max, center, distance ); + } + } + } + + private Pair boundingBoxOf( double minLon, double maxLon, double minLat, double maxLat, PointValue center, double distance ) + { + CoordinateReferenceSystem crs = center.getCoordinateReferenceSystem(); + int dimension = center.getCoordinateReferenceSystem().getDimension(); + double[] min = new double[dimension]; + double[] max = new double[dimension]; + min[0] = minLon; + min[1] = minLat; + max[0] = maxLon; + max[1] = maxLat; + if ( dimension > 2 ) + { + double[] coordinates = center.coordinate(); + for ( int i = 2; i < dimension; i++ ) + { + min[i] = coordinates[i] - distance; + max[i] = coordinates[i] + distance; + } + } + return Pair.of( Values.pointValue( crs, min ), Values.pointValue( crs, max ) ); + } + } +} + + diff --git a/community/values/src/main/java/org/neo4j/values/storable/CoordinateReferenceSystem.java b/community/values/src/main/java/org/neo4j/values/storable/CoordinateReferenceSystem.java index 5b31df2b3ff97..b0704d4b2729c 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/CoordinateReferenceSystem.java +++ b/community/values/src/main/java/org/neo4j/values/storable/CoordinateReferenceSystem.java @@ -119,7 +119,7 @@ public static CoordinateReferenceSystem get( int code ) private final int dimension; private final boolean geographic; private final Pair indexEnvelope; - private final Calculator calculator; + private final CRSCalculator calculator; private CoordinateReferenceSystem( String name, CRSTable table, int code, int dimension, boolean geographic ) { @@ -132,11 +132,11 @@ private CoordinateReferenceSystem( String name, CRSTable table, int code, int di this.indexEnvelope = envelopeFromCRS( dimension, geographic, -1000000, 1000000 ); if ( geographic ) { - this.calculator = new GeographicCalculator( dimension ); + this.calculator = new CRSCalculator.GeographicCalculator( dimension ); } else { - this.calculator = new CartesianCalculator( dimension ); + this.calculator = new CRSCalculator.CartesianCalculator( dimension ); } } @@ -184,7 +184,7 @@ public boolean isGeographic() return geographic; } - public CoordinateReferenceSystem.Calculator getCalculator() + public CRSCalculator getCalculator() { return calculator; } @@ -239,175 +239,4 @@ private static Pair envelopeFromCRS( int dimension, boolean g } return Pair.of( min, max ); } - - public interface Calculator - { - double distance( PointValue p1, PointValue p2 ); - - Pair boundingBox( PointValue center, double distance ); - } - - private static double pythagoras( double[] a, double[] b ) - { - double sqrSum = 0.0; - for ( int i = 0; i < a.length; i++ ) - { - double diff = a[i] - b[i]; - sqrSum += diff * diff; - } - return sqrt( sqrSum ); - } - - public static class CartesianCalculator implements Calculator - { - int dimension; - - CartesianCalculator( int dimension ) - { - this.dimension = dimension; - } - - @Override - public double distance( PointValue p1, PointValue p2 ) - { - assert p1.getCoordinateReferenceSystem().dimension == dimension; - assert p2.getCoordinateReferenceSystem().dimension == dimension; - return pythagoras( p1.coordinate(), p2.coordinate() ); - } - - @Override - public Pair boundingBox( PointValue center, double distance ) - { - assert center.getCoordinateReferenceSystem().dimension == dimension; - double[] coordinates = center.coordinate(); - double[] min = new double[dimension]; - double[] max = new double[dimension]; - for ( int i = 0; i < dimension; i++ ) - { - min[i] = coordinates[i] - distance; - max[i] = coordinates[i] + distance; - } - CoordinateReferenceSystem crs = center.getCoordinateReferenceSystem(); - return Pair.of( Values.pointValue( crs, min ), Values.pointValue( crs, max ) ); - } - } - - public static class GeographicCalculator implements Calculator - { - public static final double EARTH_RADIUS_METERS = 6378140.0; - private static final double EXTENSION_FACTOR = 1.0001; - int dimension; - - GeographicCalculator( int dimension ) - { - this.dimension = dimension; - } - - @Override - public double distance( PointValue p1, PointValue p2 ) - { - assert p1.getCoordinateReferenceSystem().dimension == dimension; - assert p2.getCoordinateReferenceSystem().dimension == dimension; - double[] c1Coord = p1.coordinate(); - double[] c2Coord = p2.coordinate(); - double[] c1 = new double[]{toRadians( c1Coord[0] ), toRadians( c1Coord[1] )}; - double[] c2 = new double[]{toRadians( c2Coord[0] ), toRadians( c2Coord[1] )}; - double dx = c2[0] - c1[0]; - double dy = c2[1] - c1[1]; - double alpha = pow( sin( dy / 2 ), 2.0 ) + cos( c1[1] ) * cos( c2[1] ) * pow( sin( dx / 2.0 ), 2.0 ); - double greatCircleDistance = 2.0 * atan2( sqrt( alpha ), sqrt( 1 - alpha ) ); - double distance2D = EARTH_RADIUS_METERS * greatCircleDistance; - if ( dimension > 2 ) - { - double[] a = new double[dimension - 1]; - double[] b = new double[dimension - 1]; - a[0] = distance2D; - b[0] = 0.0; - for ( int i = 1; i < dimension - 1; i++ ) - { - a[i] = 0.0; - b[i] = c1Coord[i + 1] - c2Coord[i + 1]; - } - return pythagoras( a, b ); - } - else - { - return distance2D; - } - } - - @Override - // http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates - // But calculating in degrees instead of radians to avoid rounding errors - public Pair boundingBox( PointValue center, double distance ) - { - if ( distance == 0.0 ) - { - return Pair.of( center, center ); - } - - // Extend the distance slightly to assure that all relevant points lies inside the bounding box, - // with rounding errors taken into account - double extended_distance = distance * EXTENSION_FACTOR; - - CoordinateReferenceSystem crs = center.getCoordinateReferenceSystem(); - double lat = center.coordinate()[1]; - double lon = center.coordinate()[0]; - - double r = extended_distance / EARTH_RADIUS_METERS; - - double lat_min = lat - toDegrees( r ); - double lat_max = lat + toDegrees( r ); - - // If your query circle includes one of the poles - if ( lat_max >= 90 ) - { - return boundingBoxOf( -180, 180, lat_min, 90, center, distance ); - } - else if ( lat_min <= -90 ) - { - return boundingBoxOf( -180, 180, -90, lat_max, center, distance ); - } - else - { - double delta_lon = toDegrees( asin( sin( r ) / cos( toRadians( lat ) ) ) ); - double lon_min = lon - delta_lon; - double lon_max = lon + delta_lon; - - // If you query circle wraps around the dateline - // Large rectangle covering all longitudes - // TODO implement two rectangle solution instead - if ( lon_min < -180 || lon_max > 180 ) - { - return boundingBoxOf( -180, 180, lat_min, lat_max, center, distance ); - } - else - { - return boundingBoxOf( lon_min, lon_max, lat_min, lat_max, center, distance ); - } - } - } - - private Pair boundingBoxOf( double minLon, double maxLon, double minLat, double maxLat, PointValue center, double distance ) - { - CoordinateReferenceSystem crs = center.getCoordinateReferenceSystem(); - int dimension = center.getCoordinateReferenceSystem().getDimension(); - double[] min = new double[dimension]; - double[] max = new double[dimension]; - min[0] = minLon; - min[1] = minLat; - max[0] = maxLon; - max[1] = maxLat; - if ( dimension > 2 ) - { - double[] coordinates = center.coordinate(); - for ( int i = 2; i < dimension; i++ ) - { - min[i] = coordinates[i] - distance; - max[i] = coordinates[i] + distance; - } - } - return Pair.of( Values.pointValue( crs, min ), Values.pointValue( crs, max ) ); - } - } } diff --git a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialDistanceAcceptanceTest.scala b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialDistanceAcceptanceTest.scala index 357c7008ecc61..d47af59dea7eb 100644 --- a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialDistanceAcceptanceTest.scala +++ b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialDistanceAcceptanceTest.scala @@ -304,7 +304,7 @@ class SpatialDistanceAcceptanceTest extends ExecutionEngineFunSuite with CypherC test("indexed 3D points with distance query and points within bbox") { // Given graph.createIndex("Place", "location") - setupPointsBothCRS(is3D = true) + setupPointsBothCRS(Seq(-10, 0, 10)) // <= cartesian { @@ -316,12 +316,14 @@ class SpatialDistanceAcceptanceTest extends ExecutionEngineFunSuite with CypherC // Then val expected = Set( + Map("point" -> Values.pointValue(CoordinateReferenceSystem.Cartesian_3D, 0, 0, -10)), + Map("point" -> Values.pointValue(CoordinateReferenceSystem.Cartesian_3D, 0, 9.99, 0)), Map("point" -> Values.pointValue(CoordinateReferenceSystem.Cartesian_3D, 10, 0, 0)), Map("point" -> Values.pointValue(CoordinateReferenceSystem.Cartesian_3D, 0, 10, 0)), Map("point" -> Values.pointValue(CoordinateReferenceSystem.Cartesian_3D, -10, 0, 0)), Map("point" -> Values.pointValue(CoordinateReferenceSystem.Cartesian_3D, 0, -10, 0)), Map("point" -> Values.pointValue(CoordinateReferenceSystem.Cartesian_3D, 0, 0, 0)), - Map("point" -> Values.pointValue(CoordinateReferenceSystem.Cartesian_3D, 0, 9.99, 0)) + Map("point" -> Values.pointValue(CoordinateReferenceSystem.Cartesian_3D, 0, 0, 10)) ) expectResultsAndIndexUsage(query, expected, inclusiveRange = true) } @@ -351,11 +353,13 @@ class SpatialDistanceAcceptanceTest extends ExecutionEngineFunSuite with CypherC // Then val expected = Set( + Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, 0, 0, 0)), Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, 10, 0, 0)), Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, 0, 10, 0)), Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, -10, 0, 0)), Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, 0, -10, 0)), - Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, 0, 0, 0)) + Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, 0, 0, -10)), + Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, 0, 0, 10)) ) expectResultsAndIndexUsage(query, expected, inclusiveRange = true) } @@ -370,7 +374,9 @@ class SpatialDistanceAcceptanceTest extends ExecutionEngineFunSuite with CypherC // Then val expected = Set( - Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, 0, 0, 0)) + Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, 0, 0, 0)), + Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, 0, 0, -10)), + Map("point" -> Values.pointValue(CoordinateReferenceSystem.WGS84_3D, 0, 0, 10)) ) expectResultsAndIndexUsage(query, expected, inclusiveRange = false) } @@ -449,7 +455,7 @@ class SpatialDistanceAcceptanceTest extends ExecutionEngineFunSuite with CypherC test("doughnut shape query uses the index in 3D") { // Given graph.createIndex("Place", "location") - setupPointsBothCRS(is3D = true) + setupPointsBothCRS(Seq(0)) // <= cartesian { @@ -637,8 +643,7 @@ class SpatialDistanceAcceptanceTest extends ExecutionEngineFunSuite with CypherC resultNoIndex.toList shouldBe empty } - private def setupPointsBothCRS(is3D: Boolean = false): Unit = { - val zText = if(is3D) ", z: 0" else "" + private def setupPointsBothCRS(zText: String = ""): Unit = { graph.execute(s"CREATE (p:Place) SET p.location = point({y: -10, x: -10$zText})") graph.execute(s"CREATE (p:Place) SET p.location = point({y: -10, x: 10$zText})") graph.execute(s"CREATE (p:Place) SET p.location = point({y: 10, x: -10$zText})") @@ -665,6 +670,12 @@ class SpatialDistanceAcceptanceTest extends ExecutionEngineFunSuite with CypherC Range(11, 89).foreach(i => graph.execute(s"CREATE (p:Place) SET p.location = point({latitude: $i, longitude: $i$zText})")) } + private def setupPointsBothCRS(zSet: Seq[Int]): Unit = { + zSet.foreach { z => + setupPointsBothCRS(s", z: $z") + } + } + private def expectResultsAndIndexUsage(query: String, expectedResults: Set[_ <: Any], inclusiveRange: Boolean) = { val result = executeWith(distanceConfig, query) diff --git a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialFunctionsAcceptanceTest.scala b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialFunctionsAcceptanceTest.scala index 0799caba22ecc..9d362443626b5 100644 --- a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialFunctionsAcceptanceTest.scala +++ b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialFunctionsAcceptanceTest.scala @@ -252,6 +252,7 @@ class SpatialFunctionsAcceptanceTest extends ExecutionEngineFunSuite with Cypher expectPlansToFail = Configs.AllRulePlanners)) // Then + result.toList.length should be(1) val point = result.columnAs("point").toList.head.asInstanceOf[Point] point should equal(Values.pointValue(CoordinateReferenceSystem.WGS84, 12.78, 56.7)) // And CRS names should equal @@ -283,6 +284,7 @@ class SpatialFunctionsAcceptanceTest extends ExecutionEngineFunSuite with Cypher expectPlansToFail = Configs.AllRulePlanners)) // Then + result.toList.length should be(1) val point = result.columnAs("point").toList.head.asInstanceOf[Point] point should equal(Values.pointValue(CoordinateReferenceSystem.Cartesian_3D, 1.2, 3.4, 5.6)) // And CRS names should equal @@ -350,13 +352,13 @@ class SpatialFunctionsAcceptanceTest extends ExecutionEngineFunSuite with Cypher val smaller = x <= 0 && y <= 0 && z <= 0 val larger = x >= 0 && y >= 0 && z >= 0 if (same) { - shouldCompareLike(s"point({x: $x, y: $y, z: $z, crs: $crsName})", "point({x: 0, y: 0, z: 0, crs: $crsName})", aBiggerB = false, aSmallerB = false) + shouldCompareLike(s"point({x: $x, y: $y, z: $z, crs: '$crsName'})", s"point({x: 0, y: 0, z: 0, crs: '$crsName'})", aBiggerB = false, aSmallerB = false) } else if (smaller) { - shouldCompareLike(s"point({x: $x, y: $y, z: $z, crs: $crsName})", "point({x: 0, y: 0, z: 0, crs: $crsName})", aBiggerB = false, aSmallerB = true) + shouldCompareLike(s"point({x: $x, y: $y, z: $z, crs: '$crsName'})", s"point({x: 0, y: 0, z: 0, crs: '$crsName'})", aBiggerB = false, aSmallerB = true) } else if (larger) { - shouldCompareLike(s"point({x: $x, y: $y, z: $z, crs: $crsName})", "point({x: 0, y: 0, z: 0, crs: $crsName})", aBiggerB = true, aSmallerB = false) + shouldCompareLike(s"point({x: $x, y: $y, z: $z, crs: '$crsName'})", s"point({x: 0, y: 0, z: 0, crs: '$crsName'})", aBiggerB = true, aSmallerB = false) } else { - shouldCompareLike(s"point({x: $x, y: $y, z: $z, crs: $crsName})", "point({x: 0, y: 0, z: 0, crs: $crsName})", aBiggerB = null, aSmallerB = null) + shouldCompareLike(s"point({x: $x, y: $y, z: $z, crs: '$crsName'})", s"point({x: 0, y: 0, z: 0, crs: '$crsName'})", aBiggerB = null, aSmallerB = null) } } }