blob: 9913f83c279b7c513248d50e675ce18b276fae13 [file] [log] [blame]
/*
* 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.lucene.spatial3d.geom;
import org.junit.Test;
import static org.apache.lucene.util.SloppyMath.toRadians;
import org.apache.lucene.util.LuceneTestCase;
public class GeoPathTest extends LuceneTestCase {
@Test
public void testPathDistance() {
// Start with a really simple case
GeoStandardPath p;
GeoPoint gp;
p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
p.addPoint(0.0, 0.0);
p.addPoint(0.0, 0.1);
p.addPoint(0.0, 0.2);
p.done();
gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.15);
assertEquals(Double.POSITIVE_INFINITY, p.computeDistance(DistanceStyle.ARC,gp), 0.0);
gp = new GeoPoint(PlanetModel.SPHERE, 0.05, 0.15);
assertEquals(0.15 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.12);
assertEquals(0.12 + 0.0, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
gp = new GeoPoint(PlanetModel.SPHERE, -0.15, 0.05);
assertEquals(Double.POSITIVE_INFINITY, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.25);
assertEquals(0.20 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.05);
assertEquals(0.0 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
// Compute path distances now
p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
p.addPoint(0.0, 0.0);
p.addPoint(0.0, 0.1);
p.addPoint(0.0, 0.2);
p.done();
gp = new GeoPoint(PlanetModel.SPHERE, 0.05, 0.15);
assertEquals(0.15 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
assertEquals(0.15, p.computeNearestDistance(DistanceStyle.ARC,gp), 0.000001);
assertEquals(0.10, p.computeDeltaDistance(DistanceStyle.ARC,gp), 0.000001);
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.12);
assertEquals(0.12, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
assertEquals(0.12, p.computeNearestDistance(DistanceStyle.ARC,gp), 0.000001);
assertEquals(0.0, p.computeDeltaDistance(DistanceStyle.ARC,gp), 0.000001);
// Now try a vertical path, and make sure distances are as expected
p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
p.addPoint(-Math.PI * 0.25, -0.5);
p.addPoint(Math.PI * 0.25, -0.5);
p.done();
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
assertEquals(Double.POSITIVE_INFINITY, p.computeDistance(DistanceStyle.ARC,gp), 0.0);
gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.0);
assertEquals(Double.POSITIVE_INFINITY, p.computeDistance(DistanceStyle.ARC,gp), 0.0);
gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.25 + 0.05, -0.5);
assertEquals(Math.PI * 0.5 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.25 - 0.05, -0.5);
assertEquals(0.0 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
}
@Test
public void testPathPointWithin() {
// Tests whether we can properly detect whether a point is within a path or not
GeoStandardPath p;
GeoPoint gp;
p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
// Build a diagonal path crossing the equator
p.addPoint(-0.2, -0.2);
p.addPoint(0.2, 0.2);
p.done();
// Test points on the path
gp = new GeoPoint(PlanetModel.SPHERE, -0.2, -0.2);
assertTrue(p.isWithin(gp));
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
assertTrue(p.isWithin(gp));
gp = new GeoPoint(PlanetModel.SPHERE, 0.1, 0.1);
assertTrue(p.isWithin(gp));
// Test points off the path
gp = new GeoPoint(PlanetModel.SPHERE, -0.2, 0.2);
assertFalse(p.isWithin(gp));
gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0);
assertFalse(p.isWithin(gp));
gp = new GeoPoint(PlanetModel.SPHERE, 0.2, -0.2);
assertFalse(p.isWithin(gp));
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
assertFalse(p.isWithin(gp));
// Repeat the test, but across the terminator
p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
// Build a diagonal path crossing the equator
p.addPoint(-0.2, Math.PI - 0.2);
p.addPoint(0.2, -Math.PI + 0.2);
p.done();
// Test points on the path
gp = new GeoPoint(PlanetModel.SPHERE, -0.2, Math.PI - 0.2);
assertTrue(p.isWithin(gp));
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
assertTrue(p.isWithin(gp));
gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI + 0.1);
assertTrue(p.isWithin(gp));
// Test points off the path
gp = new GeoPoint(PlanetModel.SPHERE, -0.2, -Math.PI + 0.2);
assertFalse(p.isWithin(gp));
gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0);
assertFalse(p.isWithin(gp));
gp = new GeoPoint(PlanetModel.SPHERE, 0.2, Math.PI - 0.2);
assertFalse(p.isWithin(gp));
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
assertFalse(p.isWithin(gp));
}
@Test
public void testGetRelationship() {
GeoArea rect;
GeoStandardPath p;
GeoStandardPath c;
GeoPoint point;
GeoPoint pointApprox;
int relationship;
GeoArea area;
PlanetModel planetModel;
planetModel = new PlanetModel(1.151145876105594, 0.8488541238944061);
c = new GeoStandardPath(planetModel, 0.008726646259971648);
c.addPoint(-0.6925658899376476, 0.6316613927914589);
c.addPoint(0.27828548161836364, 0.6785795524104564);
c.done();
point = new GeoPoint(planetModel,-0.49298555067758226, 0.9892440995026406);
pointApprox = new GeoPoint(0.5110940362119821, 0.7774603209946239, -0.49984312299556544);
area = GeoAreaFactory.makeGeoArea(planetModel, 0.49937141144985997, 0.5161765426256085, 0.3337218719537796,0.8544419570901649, -0.6347692823688085, 0.3069696588119369);
assertTrue(!c.isWithin(point));
// Start by testing the basic kinds of relationship, increasing in order of difficulty.
p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
p.addPoint(-0.3, -0.3);
p.addPoint(0.3, 0.3);
p.done();
// Easiest: The path is wholly contains the georect
rect = new GeoRectangle(PlanetModel.SPHERE, 0.05, -0.05, -0.05, 0.05);
assertEquals(GeoArea.CONTAINS, rect.getRelationship(p));
// Next easiest: Some endpoints of the rectangle are inside, and some are outside.
rect = new GeoRectangle(PlanetModel.SPHERE, 0.05, -0.05, -0.05, 0.5);
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
// Now, all points are outside, but the figures intersect
rect = new GeoRectangle(PlanetModel.SPHERE, 0.05, -0.05, -0.5, 0.5);
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
// Finally, all points are outside, and the figures *do not* intersect
rect = new GeoRectangle(PlanetModel.SPHERE, 0.5, -0.5, -0.5, 0.5);
assertEquals(GeoArea.WITHIN, rect.getRelationship(p));
// Check that segment edge overlap detection works
rect = new GeoRectangle(PlanetModel.SPHERE, 0.1, 0.0, -0.1, 0.0);
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
rect = new GeoRectangle(PlanetModel.SPHERE, 0.2, 0.1, -0.2, -0.1);
assertEquals(GeoArea.DISJOINT, rect.getRelationship(p));
// Check if overlap at endpoints behaves as expected next
rect = new GeoRectangle(PlanetModel.SPHERE, 0.5, -0.5, -0.5, -0.35);
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
rect = new GeoRectangle(PlanetModel.SPHERE, 0.5, -0.5, -0.5, -0.45);
assertEquals(GeoArea.DISJOINT, rect.getRelationship(p));
}
@Test
public void testPathBounds() {
GeoStandardPath c;
LatLonBounds b;
XYZBounds xyzb;
GeoPoint point;
int relationship;
GeoArea area;
PlanetModel planetModel;
planetModel = new PlanetModel(0.751521665790406,1.248478334209594);
c = new GeoStandardPath(planetModel, 0.7504915783575618);
c.addPoint(0.10869761172400265, 0.08895880215465272);
c.addPoint(0.22467878641991612, 0.10972973084229565);
c.addPoint(-0.7398772468744732, -0.4465812941383364);
c.addPoint(-0.18462055300079366, -0.6713857796763727);
c.done();
point = new GeoPoint(planetModel,-0.626645355125733,-1.409304625439381);
xyzb = new XYZBounds();
c.getBounds(xyzb);
area = GeoAreaFactory.makeGeoArea(planetModel,
xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
relationship = area.getRelationship(c);
assertTrue(relationship == GeoArea.WITHIN || relationship == GeoArea.OVERLAPS);
assertTrue(area.isWithin(point));
// No longer true due to fixed GeoStandardPath waypoints.
//assertTrue(zScaling.isWithin(point));
c = new GeoStandardPath(PlanetModel.WGS84, 0.6894050545377601);
c.addPoint(-0.0788176065762948, 0.9431251741731624);
c.addPoint(0.510387871458147, 0.5327078872484678);
c.addPoint(-0.5624521609859962, 1.5398841746888388);
c.addPoint(-0.5025171434638661, -0.5895998642788894);
c.done();
point = new GeoPoint(PlanetModel.WGS84, 0.023652082107211682, 0.023131910152748437);
//System.err.println("Point.x = "+point.x+"; point.y="+point.y+"; point.z="+point.z);
assertTrue(c.isWithin(point));
xyzb = new XYZBounds();
c.getBounds(xyzb);
area = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84,
xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
//System.err.println("minx="+xyzb.getMinimumX()+" maxx="+xyzb.getMaximumX()+" miny="+xyzb.getMinimumY()+" maxy="+xyzb.getMaximumY()+" minz="+xyzb.getMinimumZ()+" maxz="+xyzb.getMaximumZ());
//System.err.println("point.x="+point.x+" point.y="+point.y+" point.z="+point.z);
relationship = area.getRelationship(c);
assertTrue(relationship == GeoArea.WITHIN || relationship == GeoArea.OVERLAPS);
assertTrue(area.isWithin(point));
c = new GeoStandardPath(PlanetModel.WGS84, 0.7766715171374766);
c.addPoint(-0.2751718361148076, -0.7786721269011477);
c.addPoint(0.5728375851539309, -1.2700115736820465);
c.done();
point = new GeoPoint(PlanetModel.WGS84, -0.01580760332365284, -0.03956004622490505);
assertTrue(c.isWithin(point));
xyzb = new XYZBounds();
c.getBounds(xyzb);
area = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84,
xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
//System.err.println("minx="+xyzb.getMinimumX()+" maxx="+xyzb.getMaximumX()+" miny="+xyzb.getMinimumY()+" maxy="+xyzb.getMaximumY()+" minz="+xyzb.getMinimumZ()+" maxz="+xyzb.getMaximumZ());
//System.err.println("point.x="+point.x+" point.y="+point.y+" point.z="+point.z);
relationship = area.getRelationship(c);
assertTrue(relationship == GeoArea.WITHIN || relationship == GeoArea.OVERLAPS);
assertTrue(area.isWithin(point));
c = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
c.addPoint(-0.3, -0.3);
c.addPoint(0.3, 0.3);
c.done();
b = new LatLonBounds();
c.getBounds(b);
assertFalse(b.checkNoLongitudeBound());
assertFalse(b.checkNoTopLatitudeBound());
assertFalse(b.checkNoBottomLatitudeBound());
assertEquals(-0.4046919, b.getLeftLongitude(), 0.000001);
assertEquals(0.4046919, b.getRightLongitude(), 0.000001);
assertEquals(-0.3999999, b.getMinLatitude(), 0.000001);
assertEquals(0.3999999, b.getMaxLatitude(), 0.000001);
}
@Test
public void testCoLinear() {
// p1: (12,-90), p2: (11, -55), (129, -90)
GeoStandardPath p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
p.addPoint(toRadians(-90), toRadians(12));//south pole
p.addPoint(toRadians(-55), toRadians(11));
p.addPoint(toRadians(-90), toRadians(129));//south pole again
p.done();//at least test this doesn't bomb like it used too -- LUCENE-6520
}
@Test
public void testFailure1() {
/*
GeoStandardPath: {planetmodel=PlanetModel.WGS84, width=1.117010721276371(64.0), points={[
[lat=2.18531083006635E-12, lon=-3.141592653589793([X=-1.0011188539924791, Y=-1.226017000107956E-16, Z=2.187755873813378E-12])],
[lat=0.0, lon=-3.141592653589793([X=-1.0011188539924791, Y=-1.226017000107956E-16, Z=0.0])]]}}
*/
final GeoPoint[] points = new GeoPoint[]{
new GeoPoint(PlanetModel.WGS84, 2.18531083006635E-12, -3.141592653589793),
new GeoPoint(PlanetModel.WGS84, 0.0, -3.141592653589793)};
final GeoPath path;
try {
path = GeoPathFactory.makeGeoPath(PlanetModel.WGS84,
1.117010721276371, points);
} catch (IllegalArgumentException e) {
return;
}
assertTrue(false);
final GeoPoint point = new GeoPoint(PlanetModel.WGS84, -2.848117399637174E-91, -1.1092122135274942);
System.err.println("point = "+point);
final XYZBounds bounds = new XYZBounds();
path.getBounds(bounds);
final XYZSolid solid = XYZSolidFactory.makeXYZSolid(PlanetModel.WGS84,
bounds.getMinimumX(), bounds.getMaximumX(),
bounds.getMinimumY(), bounds.getMaximumY(),
bounds.getMinimumZ(), bounds.getMaximumZ());
assertTrue(path.isWithin(point));
assertTrue(solid.isWithin(point));
}
@Test
public void testInterpolation() {
final double lat = 52.51607;
final double lon = 13.37698;
final double[] pathLats = new double[] {52.5355,52.54,52.5626,52.5665,52.6007,52.6135,52.6303,52.6651,52.7074};
final double[] pathLons = new double[] {13.3634,13.3704,13.3307,13.3076,13.2806,13.2484,13.2406,13.241,13.1926};
// Set up a point in the right way
final GeoPoint carPoint = new GeoPoint(PlanetModel.SPHERE, toRadians(lat), toRadians(lon));
// Create the path, but use a tiny width (e.g. zero)
final GeoPoint[] pathPoints = new GeoPoint[pathLats.length];
for (int i = 0; i < pathPoints.length; i++) {
pathPoints[i] = new GeoPoint(PlanetModel.SPHERE, toRadians(pathLats[i]), toRadians(pathLons[i]));
}
// Construct a path with no width
final GeoPath thisPath = GeoPathFactory.makeGeoPath(PlanetModel.SPHERE, 0.0, pathPoints);
// Construct a path with a width
final GeoPath legacyPath = GeoPathFactory.makeGeoPath(PlanetModel.SPHERE, 1e-6, pathPoints);
// Compute the inside distance to the atPoint using zero-width path
final double distance = thisPath.computeNearestDistance(DistanceStyle.ARC, carPoint);
// Compute the inside distance using legacy path
final double legacyDistance = legacyPath.computeNearestDistance(DistanceStyle.ARC, carPoint);
// Compute the inside distance using the legacy formula
final double oldFormulaDistance = thisPath.computeDistance(DistanceStyle.ARC, carPoint);
// Compute the inside distance using the legacy formula with the legacy shape
final double oldFormulaLegacyDistance = legacyPath.computeDistance(DistanceStyle.ARC, carPoint);
// These should be about the same
assertEquals(legacyDistance, distance, 1e-12);
assertEquals(oldFormulaLegacyDistance, oldFormulaDistance, 1e-12);
// This isn't true because example search center is off of the path.
//assertEquals(oldFormulaDistance, distance, 1e-12);
}
@Test
public void testInterpolation2() {
final double lat = 52.5665;
final double lon = 13.3076;
final double[] pathLats = new double[] {52.5355,52.54,52.5626,52.5665,52.6007,52.6135,52.6303,52.6651,52.7074};
final double[] pathLons = new double[] {13.3634,13.3704,13.3307,13.3076,13.2806,13.2484,13.2406,13.241,13.1926};
final GeoPoint carPoint = new GeoPoint(PlanetModel.SPHERE, toRadians(lat), toRadians(lon));
final GeoPoint[] pathPoints = new GeoPoint[pathLats.length];
for (int i = 0; i < pathPoints.length; i++) {
pathPoints[i] = new GeoPoint(PlanetModel.SPHERE, toRadians(pathLats[i]), toRadians(pathLons[i]));
}
// Construct a path with no width
final GeoPath thisPath = GeoPathFactory.makeGeoPath(PlanetModel.SPHERE, 0.0, pathPoints);
// Construct a path with a width
final GeoPath legacyPath = GeoPathFactory.makeGeoPath(PlanetModel.SPHERE, 1e-6, pathPoints);
// Compute the inside distance to the atPoint using zero-width path
final double distance = thisPath.computeNearestDistance(DistanceStyle.ARC, carPoint);
// Compute the inside distance using legacy path
final double legacyDistance = legacyPath.computeNearestDistance(DistanceStyle.ARC, carPoint);
// Compute the inside distance using the legacy formula
final double oldFormulaDistance = thisPath.computeDistance(DistanceStyle.ARC, carPoint);
// Compute the inside distance using the legacy formula with the legacy shape
final double oldFormulaLegacyDistance = legacyPath.computeDistance(DistanceStyle.ARC, carPoint);
// These should be about the same
assertEquals(legacyDistance, distance, 1e-12);
assertEquals(oldFormulaLegacyDistance, oldFormulaDistance, 1e-12);
// Since the point we picked is actually on the path, this should also be true
assertEquals(oldFormulaDistance, distance, 1e-12);
}
@Test
public void testIdenticalPoints() {
PlanetModel planetModel = PlanetModel.WGS84;
GeoPoint point1 = new GeoPoint(planetModel, 1.5707963267948963, -2.4818290647609542E-148);
GeoPoint point2 = new GeoPoint(planetModel, 1.570796326794895, -3.5E-323);
GeoPoint point3 = new GeoPoint(planetModel,4.4E-323, -3.1415926535897896);
GeoPath path = GeoPathFactory.makeGeoPath(planetModel, 0, new GeoPoint[] {point1, point2, point3});
GeoPoint point = new GeoPoint(planetModel, -1.5707963267948952,2.369064805649877E-284);
//If not filtered the point is wrongly in set
assertFalse(path.isWithin(point));
//If not filtered it throws error
path = GeoPathFactory.makeGeoPath(planetModel, 1e-6, new GeoPoint[] {point1, point2, point3});
assertFalse(path.isWithin(point));
GeoPoint point4 = new GeoPoint(planetModel, 1.5, 0);
GeoPoint point5 = new GeoPoint(planetModel, 1.5, 0);
GeoPoint point6 = new GeoPoint(planetModel,4.4E-323, -3.1415926535897896);
//If not filtered creates a degenerated Vector
path = GeoPathFactory.makeGeoPath(planetModel, 0, new GeoPoint[] {point4, point5, point6});
path = GeoPathFactory.makeGeoPath(planetModel, 0.5, new GeoPoint[] {point4, point5, point6});
}
@Test
//@AwaitsFix(bugUrl="https://issues.apache.org/jira/browse/LUCENE-8696")
public void testLUCENE8696() {
GeoPoint[] points = new GeoPoint[4];
points[0] = new GeoPoint(PlanetModel.WGS84, 2.4457272005608357E-47, 0.017453291479645996);
points[1] = new GeoPoint(PlanetModel.WGS84, 2.4457272005608357E-47, 0.8952476719156919);
points[2] = new GeoPoint(PlanetModel.WGS84, 2.4457272005608357E-47, 0.6491968536639036);
points[3] = new GeoPoint(PlanetModel.WGS84, -0.7718789008737459, 0.9236607495528212);
GeoPath path = GeoPathFactory.makeGeoPath(PlanetModel.WGS84, 1.3439035240356338, points);
GeoPoint check = new GeoPoint(0.02071783020158524, 0.9523290535474472, 0.30699177256064203);
// Map to surface point, to remove that source of confusion
GeoPoint surfaceCheck = PlanetModel.WGS84.createSurfacePoint(check);
/*
[junit4] 1> cycle: cell=12502 parentCellID=12500 x: -1658490249 TO 2147483041, y: 2042111310 TO 2147483041, z: -2140282940 TO 2140277970, splits: 1 queue.size()=1
[junit4] 1> minx=-0.7731590077686981 maxx=1.0011188539924791 miny=0.9519964046486451 maxy=1.0011188539924791 minz=-0.9977622932859775 maxz=0.9977599768255027
[junit4] 1> GeoArea.CONTAINS: now addAll
*/
XYZSolid solid = XYZSolidFactory.makeXYZSolid(PlanetModel.WGS84,
-0.7731590077686981, 1.0011188539924791,
0.9519964046486451, 1.0011188539924791,
-0.9977622932859775, 0.9977599768255027);
// Verify that the point is within it
assertTrue(solid.isWithin(surfaceCheck));
// Check the (surface) relationship
int relationship = solid.getRelationship(path);
if (relationship == GeoArea.CONTAINS) {
// If relationship is CONTAINS then any point in the solid must also be within the path
// If point is within solid, it must be within shape
assertTrue(path.isWithin(surfaceCheck));
}
}
}