From 4dd402650ffd938b534736a731edb89d70b75531 Mon Sep 17 00:00:00 2001 From: David Wayne Smiley Date: Wed, 27 May 2015 04:47:31 +0000 Subject: [PATCH 1/6] git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene6487@1681905 13f79535-47bb-0310-9956-ffa450edef68 From 560f3a1a010407f9fd8ed1d84636464891248172 Mon Sep 17 00:00:00 2001 From: David Wayne Smiley Date: Wed, 27 May 2015 04:54:50 +0000 Subject: [PATCH 2/6] LUCENE-6487: Geo3D with WGS84 in-progress with David's mods (PlanetModel, and refactor of Geo3dShapeRectRelationTestCase) git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene6487@1681907 13f79535-47bb-0310-9956-ffa450edef68 --- .../lucene/spatial/spatial4j/Geo3dShape.java | 24 +- .../spatial/spatial4j/geo3d/Bounds.java | 6 +- .../spatial4j/geo3d/GeoAreaFactory.java | 4 +- .../spatial4j/geo3d/GeoBBoxFactory.java | 43 +- .../{GeoBBoxBase.java => GeoBaseBBox.java} | 22 +- .../spatial4j/geo3d/GeoBaseExtendedShape.java | 21 +- .../spatial/spatial4j/geo3d/GeoBaseShape.java | 47 ++ .../spatial/spatial4j/geo3d/GeoCircle.java | 97 ++-- .../spatial4j/geo3d/GeoConvexPolygon.java | 30 +- .../geo3d/GeoDegenerateHorizontalLine.java | 34 +- .../geo3d/GeoDegenerateLatitudeZone.java | 20 +- .../geo3d/GeoDegenerateLongitudeSlice.java | 25 +- .../spatial4j/geo3d/GeoDegeneratePoint.java | 19 +- .../geo3d/GeoDegenerateVerticalLine.java | 24 +- .../spatial4j/geo3d/GeoLatitudeZone.java | 34 +- .../spatial4j/geo3d/GeoLongitudeSlice.java | 32 +- .../spatial4j/geo3d/GeoNorthLatitudeZone.java | 25 +- .../spatial4j/geo3d/GeoNorthRectangle.java | 37 +- .../spatial/spatial4j/geo3d/GeoPath.java | 467 +++++++++++------- .../spatial/spatial4j/geo3d/GeoPoint.java | 26 +- .../spatial4j/geo3d/GeoPolygonFactory.java | 12 +- .../spatial/spatial4j/geo3d/GeoRectangle.java | 38 +- .../spatial4j/geo3d/GeoSouthLatitudeZone.java | 25 +- .../spatial4j/geo3d/GeoSouthRectangle.java | 39 +- .../GeoWideDegenerateHorizontalLine.java | 24 +- .../geo3d/GeoWideLongitudeSlice.java | 33 +- .../geo3d/GeoWideNorthRectangle.java | 37 +- .../spatial4j/geo3d/GeoWideRectangle.java | 36 +- .../geo3d/GeoWideSouthRectangle.java | 38 +- .../spatial/spatial4j/geo3d/GeoWorld.java | 16 +- .../lucene/spatial/spatial4j/geo3d/Plane.java | 377 ++++---------- .../spatial/spatial4j/geo3d/PlanetModel.java | 102 ++++ .../spatial/spatial4j/geo3d/SidedPlane.java | 29 +- .../spatial/spatial4j/geo3d/Vector.java | 24 +- .../spatial/spatial4j/Geo3dRptTest.java | 29 +- ...va => Geo3dShapeRectRelationTestCase.java} | 74 +-- ...Geo3dShapeSphereModelRectRelationTest.java | 73 +++ .../Geo3dShapeWGS84ModelRectRelationTest.java | 95 ++++ .../spatial/spatial4j/geo3d/GeoBBoxTest.java | 108 ++-- .../spatial4j/geo3d/GeoCircleTest.java | 52 +- .../spatial4j/geo3d/GeoConvexPolygonTest.java | 28 +- .../spatial/spatial4j/geo3d/GeoModelTest.java | 106 ++++ .../spatial/spatial4j/geo3d/GeoPathTest.java | 83 ++-- .../spatial4j/geo3d/GeoPolygonTest.java | 86 ++-- .../spatial/spatial4j/geo3d/PlaneTest.java | 2 +- 45 files changed, 1522 insertions(+), 1081 deletions(-) rename lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/{GeoBBoxBase.java => GeoBaseBBox.java} (81%) create mode 100644 lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseShape.java create mode 100644 lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/PlanetModel.java rename lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/{Geo3dShapeRectRelationTest.java => Geo3dShapeRectRelationTestCase.java} (74%) create mode 100644 lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java create mode 100644 lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java create mode 100644 lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoModelTest.java diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java index 34f2a12f9c2..b5855792f4a 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java @@ -28,6 +28,7 @@ import org.apache.lucene.spatial.spatial4j.geo3d.GeoArea; import org.apache.lucene.spatial.spatial4j.geo3d.GeoAreaFactory; import org.apache.lucene.spatial.spatial4j.geo3d.GeoPoint; import org.apache.lucene.spatial.spatial4j.geo3d.GeoShape; +import org.apache.lucene.spatial.spatial4j.geo3d.PlanetModel; /** * A 3D planar geometry based Spatial4j Shape implementation. @@ -38,17 +39,23 @@ public class Geo3dShape implements Shape { public final SpatialContext ctx; public final GeoShape shape; + public final PlanetModel planetModel; private Rectangle boundingBox = null; public final static double RADIANS_PER_DEGREE = Math.PI / 180.0; public final static double DEGREES_PER_RADIAN = 1.0 / RADIANS_PER_DEGREE; - public Geo3dShape(GeoShape shape, SpatialContext ctx) { + public Geo3dShape(final GeoShape shape, final SpatialContext ctx) { + this(PlanetModel.SPHERE, shape, ctx); + } + + public Geo3dShape(final PlanetModel planetModel, final GeoShape shape, final SpatialContext ctx) { if (!ctx.isGeo()) { throw new IllegalArgumentException("SpatialContext.isGeo() must be true"); } this.ctx = ctx; + this.planetModel = planetModel; this.shape = shape; } @@ -64,7 +71,8 @@ public class Geo3dShape implements Shape { protected SpatialRelation relate(Rectangle r) { // Construct the right kind of GeoArea first - GeoArea geoArea = GeoAreaFactory.makeGeoArea(r.getMaxY() * RADIANS_PER_DEGREE, + GeoArea geoArea = GeoAreaFactory.makeGeoArea(planetModel, + r.getMaxY() * RADIANS_PER_DEGREE, r.getMinY() * RADIANS_PER_DEGREE, r.getMinX() * RADIANS_PER_DEGREE, r.getMaxX() * RADIANS_PER_DEGREE); @@ -83,7 +91,7 @@ public class Geo3dShape implements Shape { protected SpatialRelation relate(Point p) { // Create a GeoPoint - GeoPoint point = new GeoPoint(p.getY()*RADIANS_PER_DEGREE, p.getX()*RADIANS_PER_DEGREE); + GeoPoint point = new GeoPoint(planetModel, p.getY()*RADIANS_PER_DEGREE, p.getX()*RADIANS_PER_DEGREE); if (shape.isWithin(point)) { // Point within shape return SpatialRelation.CONTAINS; @@ -91,7 +99,9 @@ public class Geo3dShape implements Shape { return SpatialRelation.DISJOINT; } - protected final double ROUNDOFF_ADJUSTMENT = 0.01; + // The required size of this adjustment depends on the actual planetary model chosen. + // This value is big enough to account for WGS84. + protected final double ROUNDOFF_ADJUSTMENT = 0.05; @Override public Rectangle getBoundingBox() { @@ -150,7 +160,7 @@ public class Geo3dShape implements Shape { @Override public String toString() { - return "Geo3dShape{" + shape + '}'; + return "Geo3dShape{planetmodel=" + planetModel+", shape="+shape + '}'; } @Override @@ -158,11 +168,11 @@ public class Geo3dShape implements Shape { if (!(other instanceof Geo3dShape)) return false; Geo3dShape tr = (Geo3dShape)other; - return tr.shape.equals(shape); + return tr.planetModel.equals(planetModel) && tr.shape.equals(shape); } @Override public int hashCode() { - return shape.hashCode(); + return planetModel.hashCode() + shape.hashCode(); } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Bounds.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Bounds.java index cee58aefc65..82ba62c442e 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Bounds.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Bounds.java @@ -254,11 +254,11 @@ public class Bounds { } } - public Bounds addPoint(Vector v) { + public Bounds addPoint(final Vector v) { return addPoint(v.x, v.y, v.z); } - public Bounds addPoint(double x, double y, double z) { + public Bounds addPoint(final double x, final double y, final double z) { if (!noLongitudeBound) { // Get a longitude value double longitude = Math.atan2(y, x); @@ -267,7 +267,7 @@ public class Bounds { } if (!noTopLatitudeBound || !noBottomLatitudeBound) { // Compute a latitude value - double latitude = Math.asin(z); + double latitude = Math.asin(z/Math.sqrt(z * z + x * x + y * y)); addLatitudeBound(latitude); } return this; diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoAreaFactory.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoAreaFactory.java index ab49cad3587..c273e9e3649 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoAreaFactory.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoAreaFactory.java @@ -35,8 +35,8 @@ public class GeoAreaFactory { * @param rightLon is the right longitude * @return a GeoArea corresponding to what was specified. */ - public static GeoArea makeGeoArea(double topLat, double bottomLat, double leftLon, double rightLon) { - return GeoBBoxFactory.makeGeoBBox(topLat, bottomLat, leftLon, rightLon); + public static GeoArea makeGeoArea(final PlanetModel planetModel, final double topLat, final double bottomLat, final double leftLon, final double rightLon) { + return GeoBBoxFactory.makeGeoBBox(planetModel, topLat, bottomLat, leftLon, rightLon); } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxFactory.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxFactory.java index 8e2b8df9276..863aca57d1b 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxFactory.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxFactory.java @@ -29,13 +29,14 @@ public class GeoBBoxFactory { /** * Create a geobbox of the right kind given the specified bounds. * + * @param planetModel is the planet model * @param topLat is the top latitude * @param bottomLat is the bottom latitude * @param leftLon is the left longitude * @param rightLon is the right longitude * @return a GeoBBox corresponding to what was specified. */ - public static GeoBBox makeGeoBBox(double topLat, double bottomLat, double leftLon, double rightLon) { + public static GeoBBox makeGeoBBox(final PlanetModel planetModel, double topLat, double bottomLat, double leftLon, double rightLon) { //System.err.println("Making rectangle for topLat="+topLat*180.0/Math.PI+", bottomLat="+bottomLat*180.0/Math.PI+", leftLon="+leftLon*180.0/Math.PI+", rightlon="+rightLon*180.0/Math.PI); if (topLat > Math.PI * 0.5) topLat = Math.PI * 0.5; @@ -47,17 +48,17 @@ public class GeoBBoxFactory { rightLon = Math.PI; if (Math.abs(leftLon + Math.PI) < Vector.MINIMUM_RESOLUTION && Math.abs(rightLon - Math.PI) < Vector.MINIMUM_RESOLUTION) { if (Math.abs(topLat - Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION && Math.abs(bottomLat + Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION) - return new GeoWorld(); + return new GeoWorld(planetModel); if (Math.abs(topLat - bottomLat) < Vector.MINIMUM_RESOLUTION) { if (Math.abs(topLat - Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION || Math.abs(topLat + Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION) - return new GeoDegeneratePoint(topLat, 0.0); - return new GeoDegenerateLatitudeZone(topLat); + return new GeoDegeneratePoint(planetModel, topLat, 0.0); + return new GeoDegenerateLatitudeZone(planetModel, topLat); } if (Math.abs(topLat - Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION) - return new GeoNorthLatitudeZone(bottomLat); + return new GeoNorthLatitudeZone(planetModel, bottomLat); else if (Math.abs(bottomLat + Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION) - return new GeoSouthLatitudeZone(topLat); - return new GeoLatitudeZone(topLat, bottomLat); + return new GeoSouthLatitudeZone(planetModel, topLat); + return new GeoLatitudeZone(planetModel, topLat, bottomLat); } //System.err.println(" not latitude zone"); double extent = rightLon - leftLon; @@ -65,47 +66,47 @@ public class GeoBBoxFactory { extent += Math.PI * 2.0; if (topLat == Math.PI * 0.5 && bottomLat == -Math.PI * 0.5) { if (Math.abs(leftLon - rightLon) < Vector.MINIMUM_RESOLUTION) - return new GeoDegenerateLongitudeSlice(leftLon); + return new GeoDegenerateLongitudeSlice(planetModel, leftLon); if (extent >= Math.PI) - return new GeoWideLongitudeSlice(leftLon, rightLon); + return new GeoWideLongitudeSlice(planetModel, leftLon, rightLon); - return new GeoLongitudeSlice(leftLon, rightLon); + return new GeoLongitudeSlice(planetModel, leftLon, rightLon); } //System.err.println(" not longitude slice"); if (Math.abs(leftLon - rightLon) < Vector.MINIMUM_RESOLUTION) { if (Math.abs(topLat - bottomLat) < Vector.MINIMUM_RESOLUTION) - return new GeoDegeneratePoint(topLat, leftLon); - return new GeoDegenerateVerticalLine(topLat, bottomLat, leftLon); + return new GeoDegeneratePoint(planetModel, topLat, leftLon); + return new GeoDegenerateVerticalLine(planetModel, topLat, bottomLat, leftLon); } //System.err.println(" not vertical line"); if (extent >= Math.PI) { if (Math.abs(topLat - bottomLat) < Vector.MINIMUM_RESOLUTION) { //System.err.println(" wide degenerate line"); - return new GeoWideDegenerateHorizontalLine(topLat, leftLon, rightLon); + return new GeoWideDegenerateHorizontalLine(planetModel, topLat, leftLon, rightLon); } if (Math.abs(topLat - Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION) { - return new GeoWideNorthRectangle(bottomLat, leftLon, rightLon); + return new GeoWideNorthRectangle(planetModel, bottomLat, leftLon, rightLon); } else if (Math.abs(bottomLat + Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION) { - return new GeoWideSouthRectangle(topLat, leftLon, rightLon); + return new GeoWideSouthRectangle(planetModel, topLat, leftLon, rightLon); } //System.err.println(" wide rect"); - return new GeoWideRectangle(topLat, bottomLat, leftLon, rightLon); + return new GeoWideRectangle(planetModel, topLat, bottomLat, leftLon, rightLon); } if (Math.abs(topLat - bottomLat) < Vector.MINIMUM_RESOLUTION) { if (Math.abs(topLat - Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION || Math.abs(topLat + Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION) { - return new GeoDegeneratePoint(topLat, 0.0); + return new GeoDegeneratePoint(planetModel, topLat, 0.0); } //System.err.println(" horizontal line"); - return new GeoDegenerateHorizontalLine(topLat, leftLon, rightLon); + return new GeoDegenerateHorizontalLine(planetModel, topLat, leftLon, rightLon); } if (Math.abs(topLat - Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION) { - return new GeoNorthRectangle(bottomLat, leftLon, rightLon); + return new GeoNorthRectangle(planetModel, bottomLat, leftLon, rightLon); } else if (Math.abs(bottomLat + Math.PI * 0.5) < Vector.MINIMUM_RESOLUTION) { - return new GeoSouthRectangle(topLat, leftLon, rightLon); + return new GeoSouthRectangle(planetModel, topLat, leftLon, rightLon); } //System.err.println(" rectangle"); - return new GeoRectangle(topLat, bottomLat, leftLon, rightLon); + return new GeoRectangle(planetModel, topLat, bottomLat, leftLon, rightLon); } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxBase.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseBBox.java similarity index 81% rename from lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxBase.java rename to lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseBBox.java index 1e1a603e7d7..1e52aaf734f 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxBase.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseBBox.java @@ -23,11 +23,12 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public abstract class GeoBBoxBase implements GeoBBox { - - protected final static GeoPoint NORTH_POLE = new GeoPoint(0.0, 0.0, 1.0); - protected final static GeoPoint SOUTH_POLE = new GeoPoint(0.0, 0.0, -1.0); +public abstract class GeoBaseBBox extends GeoBaseShape implements GeoBBox { + public GeoBaseBBox(final PlanetModel planetModel) { + super(planetModel); + } + @Override public abstract boolean isWithin(final Vector point); @@ -45,6 +46,9 @@ public abstract class GeoBBoxBase implements GeoBBox { } else { foundOutside = true; } + if (foundInside && foundOutside) { + return SOME_INSIDE; + } } if (!foundInside && !foundOutside) return NONE_INSIDE; @@ -54,5 +58,15 @@ public abstract class GeoBBoxBase implements GeoBBox { return NONE_INSIDE; return SOME_INSIDE; } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(final Object o) { + return super.equals(o); + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseExtendedShape.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseExtendedShape.java index 10572a8cda9..e3a1c0347f9 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseExtendedShape.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseExtendedShape.java @@ -22,11 +22,10 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public abstract class GeoBaseExtendedShape implements GeoShape { - protected final static GeoPoint NORTH_POLE = new GeoPoint(0.0, 0.0, 1.0); - protected final static GeoPoint SOUTH_POLE = new GeoPoint(0.0, 0.0, -1.0); +public abstract class GeoBaseExtendedShape extends GeoBaseShape implements GeoShape { - public GeoBaseExtendedShape() { + public GeoBaseExtendedShape(final PlanetModel planetModel) { + super(planetModel); } /** @@ -83,12 +82,22 @@ public abstract class GeoBaseExtendedShape implements GeoShape { public Bounds getBounds(Bounds bounds) { if (bounds == null) bounds = new Bounds(); - if (isWithin(NORTH_POLE)) { + if (isWithin(planetModel.NORTH_POLE)) { bounds.noTopLatitudeBound().noLongitudeBound(); } - if (isWithin(SOUTH_POLE)) { + if (isWithin(planetModel.SOUTH_POLE)) { bounds.noBottomLatitudeBound().noLongitudeBound(); } return bounds; } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public boolean equals(final Object o) { + return super.equals(o); + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseShape.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseShape.java new file mode 100644 index 00000000000..f5a3dad017b --- /dev/null +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseShape.java @@ -0,0 +1,47 @@ +package org.apache.lucene.spatial.spatial4j.geo3d; + +/* + * 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. + */ + +/** + * All bounding box shapes can derive from this base class, which furnishes + * some common code + * + * @lucene.internal + */ +public abstract class GeoBaseShape { + + protected final PlanetModel planetModel; + + public GeoBaseShape(final PlanetModel planetModel) { + this.planetModel = planetModel; + } + + @Override + public int hashCode() { + return planetModel.hashCode(); + } + + @Override + public boolean equals(final Object o) { + if (!(o instanceof GeoBaseShape)) + return false; + return planetModel.equals(((GeoBaseShape)o).planetModel); + } +} + + diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircle.java index b04d0faa4c4..23d828836b3 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircle.java @@ -25,45 +25,42 @@ package org.apache.lucene.spatial.spatial4j.geo3d; public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, GeoSizeable { public final GeoPoint center; public final double cutoffAngle; - public final double cutoffNormalDistance; - public final double cutoffLinearDistance; public final SidedPlane circlePlane; public final GeoPoint[] edgePoints; public static final GeoPoint[] circlePoints = new GeoPoint[0]; - public GeoCircle(final double lat, final double lon, final double cutoffAngle) { - super(); + public GeoCircle(final PlanetModel planetModel, final double lat, final double lon, final double cutoffAngle) { + super(planetModel); if (lat < -Math.PI * 0.5 || lat > Math.PI * 0.5) throw new IllegalArgumentException("Latitude out of bounds"); if (lon < -Math.PI || lon > Math.PI) throw new IllegalArgumentException("Longitude out of bounds"); if (cutoffAngle <= 0.0 || cutoffAngle > Math.PI) throw new IllegalArgumentException("Cutoff angle out of bounds"); - final double sinAngle = Math.sin(cutoffAngle); final double cosAngle = Math.cos(cutoffAngle); - this.center = new GeoPoint(lat, lon); - this.cutoffNormalDistance = sinAngle; - // Need the chord distance. This is just the chord distance: sqrt((1 - cos(angle))^2 + (sin(angle))^2). - final double xDiff = 1.0 - cosAngle; - this.cutoffLinearDistance = Math.sqrt(xDiff * xDiff + sinAngle * sinAngle); + this.center = new GeoPoint(planetModel, lat, lon); + final double magnitude = center.magnitude(); + // In an ellipsoidal world, cutoff distances make no sense, unfortunately. Only membership + // can be used to make in/out determination. this.cutoffAngle = cutoffAngle; - this.circlePlane = new SidedPlane(center, center, -cosAngle); + // The plane's normal vector needs to be normalized, since we compute D on that basis + this.circlePlane = new SidedPlane(center, center.normalize(), -cosAngle * magnitude); // Compute a point on the circle boundary. if (cutoffAngle == Math.PI) this.edgePoints = new GeoPoint[0]; else { - // Move from center only in latitude. Then, if we go past the north pole, adjust the longitude also. - double newLat = lat + cutoffAngle; - double newLon = lon; - if (newLat > Math.PI * 0.5) { - newLat = Math.PI - newLat; - newLon += Math.PI; + // We already have circle plane, which is the definitive determination of the edge of the "circle". + // Next, compute vertical plane going through origin and the center point (C = 0, D = 0). + Plane verticalPlane = Plane.constructNormalizedVerticalPlane(this.center.x, this.center.y); + if (verticalPlane == null) { + verticalPlane = new Plane(1.0,0.0); } - while (newLon > Math.PI) { - newLon -= Math.PI * 2.0; + // Finally, use Plane.findIntersections() to find the intersection points. + final GeoPoint edgePoint = this.circlePlane.getSampleIntersectionPoint(planetModel, verticalPlane); + if (edgePoint == null) { + throw new RuntimeException("Could not find edge point for circle at lat="+lat+" lon="+lon+" cutoffAngle="+cutoffAngle+" planetModel="+planetModel); } - final GeoPoint edgePoint = new GeoPoint(newLat, newLon); //if (Math.abs(circlePlane.evaluate(edgePoint)) > 1e-10) // throw new RuntimeException("Computed an edge point that does not satisfy circlePlane equation! "+circlePlane.evaluate(edgePoint)); this.edgePoints = new GeoPoint[]{edgePoint}; @@ -92,10 +89,9 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, */ @Override public double computeNormalDistance(final GeoPoint point) { - double normalDistance = this.center.normalDistance(point); - if (normalDistance > cutoffNormalDistance) + if (!isWithin(point)) return Double.MAX_VALUE; - return normalDistance; + return this.center.normalDistance(point); } /** @@ -105,10 +101,9 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, */ @Override public double computeNormalDistance(final double x, final double y, final double z) { - double normalDistance = this.center.normalDistance(x, y, z); - if (normalDistance > cutoffNormalDistance) + if (!isWithin(x,y,z)) return Double.MAX_VALUE; - return normalDistance; + return this.center.normalDistance(x, y, z); } /** @@ -118,10 +113,9 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, */ @Override public double computeSquaredNormalDistance(final GeoPoint point) { - double normalDistanceSquared = this.center.normalDistanceSquared(point); - if (normalDistanceSquared > cutoffNormalDistance * cutoffNormalDistance) + if (!isWithin(point)) return Double.MAX_VALUE; - return normalDistanceSquared; + return this.center.normalDistanceSquared(point); } /** @@ -131,10 +125,9 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, */ @Override public double computeSquaredNormalDistance(final double x, final double y, final double z) { - double normalDistanceSquared = this.center.normalDistanceSquared(x, y, z); - if (normalDistanceSquared > cutoffNormalDistance * cutoffNormalDistance) + if (!isWithin(x,y,z)) return Double.MAX_VALUE; - return normalDistanceSquared; + return this.center.normalDistanceSquared(x, y, z); } /** @@ -143,10 +136,9 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, */ @Override public double computeLinearDistance(final GeoPoint point) { - double linearDistance = this.center.linearDistance(point); - if (linearDistance > cutoffLinearDistance) + if (!isWithin(point)) return Double.MAX_VALUE; - return linearDistance; + return this.center.linearDistance(point); } /** @@ -155,10 +147,9 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, */ @Override public double computeLinearDistance(final double x, final double y, final double z) { - double linearDistance = this.center.linearDistance(x, y, z); - if (linearDistance > cutoffLinearDistance) + if (!isWithin(x,y,z)) return Double.MAX_VALUE; - return linearDistance; + return this.center.linearDistance(x, y, z); } /** @@ -166,10 +157,9 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, */ @Override public double computeSquaredLinearDistance(final GeoPoint point) { - double linearDistanceSquared = this.center.linearDistanceSquared(point); - if (linearDistanceSquared > cutoffLinearDistance * cutoffLinearDistance) + if (!isWithin(point)) return Double.MAX_VALUE; - return linearDistanceSquared; + return this.center.linearDistanceSquared(point); } /** @@ -177,10 +167,9 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, */ @Override public double computeSquaredLinearDistance(final double x, final double y, final double z) { - double linearDistanceSquared = this.center.linearDistanceSquared(x, y, z); - if (linearDistanceSquared > cutoffLinearDistance * cutoffLinearDistance) + if (!isWithin(x,y,z)) return Double.MAX_VALUE; - return linearDistanceSquared; + return this.center.linearDistanceSquared(x, y, z); } /** @@ -189,10 +178,9 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, */ @Override public double computeArcDistance(final GeoPoint point) { - double dist = this.center.arcDistance(point); - if (dist > cutoffAngle) + if (!isWithin(point)) return Double.MAX_VALUE; - return dist; + return this.center.arcDistance(point); } @Override @@ -214,7 +202,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { - return circlePlane.intersects(p, notablePoints, circlePoints, bounds); + return circlePlane.intersects(planetModel, p, notablePoints, circlePoints, bounds); } /** @@ -230,7 +218,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, public Bounds getBounds(Bounds bounds) { bounds = super.getBounds(bounds); bounds.addPoint(center); - circlePlane.recordBounds(bounds); + circlePlane.recordBounds(planetModel, bounds); return bounds; } @@ -239,21 +227,20 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, if (!(o instanceof GeoCircle)) return false; GeoCircle other = (GeoCircle) o; - return other.center.equals(center) && other.cutoffAngle == cutoffAngle; + return super.equals(other) && other.center.equals(center) && other.cutoffAngle == cutoffAngle; } @Override public int hashCode() { - int result; - long temp; - result = center.hashCode(); - temp = Double.doubleToLongBits(cutoffAngle); + int result = super.hashCode(); + result = 31 * result + center.hashCode(); + long temp = Double.doubleToLongBits(cutoffAngle); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public String toString() { - return "GeoCircle: {center=" + center + ", radius=" + cutoffAngle + "(" + cutoffAngle * 180.0 / Math.PI + ")}"; + return "GeoCircle: {planetmodel=" + planetModel+", center=" + center + ", radius=" + cutoffAngle + "(" + cutoffAngle * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygon.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygon.java index cf9a1d7611f..26a763a69eb 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygon.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygon.java @@ -45,7 +45,8 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers * Create a convex polygon from a list of points. The first point must be on the * external edge. */ - public GeoConvexPolygon(final List pointList) { + public GeoConvexPolygon(final PlanetModel planetModel, final List pointList) { + super(planetModel); this.points = pointList; this.isInternalEdges = null; donePoints(false); @@ -55,7 +56,8 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers * Create a convex polygon from a list of points, keeping track of which boundaries * are internal. This is used when creating a polygon as a building block for another shape. */ - public GeoConvexPolygon(final List pointList, final BitSet internalEdgeFlags, final boolean returnEdgeInternal) { + public GeoConvexPolygon(final PlanetModel planetModel, final List pointList, final BitSet internalEdgeFlags, final boolean returnEdgeInternal) { + super(planetModel); this.points = pointList; this.isInternalEdges = internalEdgeFlags; donePoints(returnEdgeInternal); @@ -65,7 +67,8 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers * Create a convex polygon, with a starting latitude and longitude. * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} */ - public GeoConvexPolygon(final double startLatitude, final double startLongitude) { + public GeoConvexPolygon(final PlanetModel planetModel, final double startLatitude, final double startLongitude) { + super(planetModel); points = new ArrayList(); isInternalEdges = new BitSet(); // Argument checking @@ -74,7 +77,7 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers if (startLongitude < -Math.PI || startLongitude > Math.PI) throw new IllegalArgumentException("Longitude out of range"); - final GeoPoint p = new GeoPoint(startLatitude, startLongitude); + final GeoPoint p = new GeoPoint(planetModel, startLatitude, startLongitude); points.add(p); } @@ -94,7 +97,7 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers if (longitude < -Math.PI || longitude > Math.PI) throw new IllegalArgumentException("Longitude out of range"); - final GeoPoint p = new GeoPoint(latitude, longitude); + final GeoPoint p = new GeoPoint(planetModel, latitude, longitude); isInternalEdges.set(points.size(), isInternalEdge); points.add(p); } @@ -191,7 +194,7 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers membershipBounds[count++] = edges[otherIndex]; } } - if (edge.intersects(p, notablePoints, points, bounds, membershipBounds)) { + if (edge.intersects(planetModel, p, notablePoints, points, bounds, membershipBounds)) { //System.err.println(" intersects!"); return true; } @@ -230,7 +233,7 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers membershipBounds[count++] = edges[otherIndex]; } } - edge.recordBounds(bounds, membershipBounds); + edge.recordBounds(planetModel, bounds, membershipBounds); } if (fullDistance >= Math.PI) { @@ -245,6 +248,8 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers if (!(o instanceof GeoConvexPolygon)) return false; GeoConvexPolygon other = (GeoConvexPolygon) o; + if (!super.equals(other)) + return false; if (other.points.size() != points.size()) return false; @@ -257,17 +262,14 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers @Override public int hashCode() { - return points.hashCode(); + int result = super.hashCode(); + result = 31 * result + points.hashCode(); + return result; } @Override public String toString() { - StringBuilder edgeString = new StringBuilder("{"); - for (int i = 0; i < edges.length; i++) { - edgeString.append(edges[i]).append(" internal? ").append(internalEdges[i]).append("; "); - } - edgeString.append("}"); - return "GeoConvexPolygon: {points=" + points + " edges=" + edgeString + "}"; + return "GeoConvexPolygon: {planetmodel=" + planetModel + ", points=" + points + "}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateHorizontalLine.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateHorizontalLine.java index 14a7396b4da..e1fd4ea2cd9 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateHorizontalLine.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateHorizontalLine.java @@ -24,7 +24,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoDegenerateHorizontalLine extends GeoBBoxBase { +public class GeoDegenerateHorizontalLine extends GeoBaseBBox { public final double latitude; public final double leftLon; public final double rightLon; @@ -44,7 +44,8 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase { /** * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} */ - public GeoDegenerateHorizontalLine(final double latitude, final double leftLon, double rightLon) { + public GeoDegenerateHorizontalLine(final PlanetModel planetModel, final double latitude, final double leftLon, double rightLon) { + super(planetModel); // Argument checking if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5) throw new IllegalArgumentException("Latitude out of range"); @@ -71,10 +72,10 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase { final double cosRightLon = Math.cos(rightLon); // Now build the two points - this.LHC = new GeoPoint(sinLatitude, sinLeftLon, cosLatitude, cosLeftLon); - this.RHC = new GeoPoint(sinLatitude, sinRightLon, cosLatitude, cosRightLon); + this.LHC = new GeoPoint(planetModel, sinLatitude, sinLeftLon, cosLatitude, cosLeftLon); + this.RHC = new GeoPoint(planetModel, sinLatitude, sinRightLon, cosLatitude, cosRightLon); - this.plane = new Plane(sinLatitude); + this.plane = new Plane(planetModel, sinLatitude); // Normalize while (leftLon > rightLon) { @@ -84,7 +85,7 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase { final double sinMiddleLon = Math.sin(middleLon); final double cosMiddleLon = Math.cos(middleLon); - this.centerPoint = new GeoPoint(sinLatitude, sinMiddleLon, cosLatitude, cosMiddleLon); + this.centerPoint = new GeoPoint(planetModel, sinLatitude, sinMiddleLon, cosLatitude, cosMiddleLon); this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); @@ -107,7 +108,7 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon); } @Override @@ -148,7 +149,7 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase { @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { - return p.intersects(plane, notablePoints, planePoints, bounds, leftPlane, rightPlane); + return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, leftPlane, rightPlane); } /** @@ -170,12 +171,18 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase { @Override public int getRelationship(final GeoShape path) { - if (path.intersects(plane, planePoints, leftPlane, rightPlane)) + //System.err.println("getting relationship between "+this+" and "+path); + if (path.intersects(plane, planePoints, leftPlane, rightPlane)) { + //System.err.println(" overlaps"); return OVERLAPS; + } - if (path.isWithin(centerPoint)) + if (path.isWithin(centerPoint)) { + //System.err.println(" contains"); return CONTAINS; + } + //System.err.println(" disjoint"); return DISJOINT; } @@ -184,19 +191,20 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase { if (!(o instanceof GeoDegenerateHorizontalLine)) return false; GeoDegenerateHorizontalLine other = (GeoDegenerateHorizontalLine) o; - return other.LHC.equals(LHC) && other.RHC.equals(RHC); + return super.equals(other) && other.LHC.equals(LHC) && other.RHC.equals(RHC); } @Override public int hashCode() { - int result = LHC.hashCode(); + int result = super.hashCode(); + result = 31 * result + LHC.hashCode(); result = 31 * result + RHC.hashCode(); return result; } @Override public String toString() { - return "GeoDegenerateHorizontalLine: {latitude=" + latitude + "(" + latitude * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightLon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + return "GeoDegenerateHorizontalLine: {planetmodel="+planetModel+", latitude=" + latitude + "(" + latitude * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightLon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLatitudeZone.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLatitudeZone.java index 982f8f593b6..31bd77b80f6 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLatitudeZone.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLatitudeZone.java @@ -23,7 +23,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoDegenerateLatitudeZone extends GeoBBoxBase { +public class GeoDegenerateLatitudeZone extends GeoBaseBBox { public final double latitude; public final double sinLatitude; @@ -32,14 +32,15 @@ public class GeoDegenerateLatitudeZone extends GeoBBoxBase { public final GeoPoint[] edgePoints; public final static GeoPoint[] planePoints = new GeoPoint[0]; - public GeoDegenerateLatitudeZone(final double latitude) { + public GeoDegenerateLatitudeZone(final PlanetModel planetModel, final double latitude) { + super(planetModel); this.latitude = latitude; this.sinLatitude = Math.sin(latitude); double cosLatitude = Math.cos(latitude); - this.plane = new Plane(sinLatitude); + this.plane = new Plane(planetModel, sinLatitude); // Compute an interior point. - interiorPoint = new GeoPoint(cosLatitude, 0.0, sinLatitude); + interiorPoint = new GeoPoint(planetModel, sinLatitude, 0.0, cosLatitude, 1.0); edgePoints = new GeoPoint[]{interiorPoint}; } @@ -47,7 +48,7 @@ public class GeoDegenerateLatitudeZone extends GeoBBoxBase { public GeoBBox expand(final double angle) { double newTopLat = latitude + angle; double newBottomLat = latitude - angle; - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, -Math.PI, Math.PI); } @Override @@ -83,7 +84,7 @@ public class GeoDegenerateLatitudeZone extends GeoBBoxBase { @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { - return p.intersects(plane, notablePoints, planePoints, bounds); + return p.intersects(planetModel, plane, notablePoints, planePoints, bounds); } /** @@ -125,19 +126,20 @@ public class GeoDegenerateLatitudeZone extends GeoBBoxBase { if (!(o instanceof GeoDegenerateLatitudeZone)) return false; GeoDegenerateLatitudeZone other = (GeoDegenerateLatitudeZone) o; - return other.latitude == latitude; + return super.equals(other) && other.latitude == latitude; } @Override public int hashCode() { + int result = super.hashCode(); long temp = Double.doubleToLongBits(latitude); - int result = (int) (temp ^ (temp >>> 32)); + result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public String toString() { - return "GeoDegenerateLatitudeZone: {lat=" + latitude + "(" + latitude * 180.0 / Math.PI + ")}"; + return "GeoDegenerateLatitudeZone: {planetmodel="+planetModel+", lat=" + latitude + "(" + latitude * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLongitudeSlice.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLongitudeSlice.java index 69c703b862e..1e41aca2283 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLongitudeSlice.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLongitudeSlice.java @@ -22,7 +22,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoDegenerateLongitudeSlice extends GeoBBoxBase { +public class GeoDegenerateLongitudeSlice extends GeoBaseBBox { public final double longitude; public final double sinLongitude; @@ -32,12 +32,13 @@ public class GeoDegenerateLongitudeSlice extends GeoBBoxBase { public final GeoPoint interiorPoint; public final GeoPoint[] edgePoints; - public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE, SOUTH_POLE}; + public final GeoPoint[] planePoints; /** * Accepts only values in the following ranges: lon: {@code -PI -> PI} */ - public GeoDegenerateLongitudeSlice(final double longitude) { + public GeoDegenerateLongitudeSlice(final PlanetModel planetModel, final double longitude) { + super(planetModel); // Argument checking if (longitude < -Math.PI || longitude > Math.PI) throw new IllegalArgumentException("Longitude out of range"); @@ -48,9 +49,10 @@ public class GeoDegenerateLongitudeSlice extends GeoBBoxBase { this.plane = new Plane(cosLongitude, sinLongitude); // We need a bounding plane too, which is perpendicular to the longitude plane and sided so that the point (0.0, longitude) is inside. - this.interiorPoint = new GeoPoint(cosLongitude, sinLongitude, 0.0); + this.interiorPoint = new GeoPoint(planetModel, 0.0, sinLongitude, 1.0, cosLongitude); this.boundingPlane = new SidedPlane(interiorPoint, -sinLongitude, cosLongitude); this.edgePoints = new GeoPoint[]{interiorPoint}; + this.planePoints = new GeoPoint[]{planetModel.NORTH_POLE, planetModel.SOUTH_POLE}; } @Override @@ -63,7 +65,7 @@ public class GeoDegenerateLongitudeSlice extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, Math.PI * 0.5, -Math.PI * 0.5, newLeftLon, newRightLon); } @Override @@ -100,7 +102,7 @@ public class GeoDegenerateLongitudeSlice extends GeoBBoxBase { @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { - return p.intersects(plane, notablePoints, planePoints, bounds, boundingPlane); + return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, boundingPlane); } /** @@ -138,21 +140,20 @@ public class GeoDegenerateLongitudeSlice extends GeoBBoxBase { if (!(o instanceof GeoDegenerateLongitudeSlice)) return false; GeoDegenerateLongitudeSlice other = (GeoDegenerateLongitudeSlice) o; - return other.longitude == longitude; + return super.equals(other) && other.longitude == longitude; } @Override public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(longitude); - result = (int) (temp ^ (temp >>> 32)); + int result = super.hashCode(); + long temp = Double.doubleToLongBits(longitude); + result = result * 31 + (int) (temp ^ (temp >>> 32)); return result; } @Override public String toString() { - return "GeoDegenerateLongitudeSlice: {longitude=" + longitude + "(" + longitude * 180.0 / Math.PI + ")}"; + return "GeoDegenerateLongitudeSlice: {planetmodel="+planetModel+", longitude=" + longitude + "(" + longitude * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegeneratePoint.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegeneratePoint.java index 2f76ea835a4..258b8dd1951 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegeneratePoint.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegeneratePoint.java @@ -26,10 +26,12 @@ package org.apache.lucene.spatial.spatial4j.geo3d; public class GeoDegeneratePoint extends GeoPoint implements GeoBBox { public final double latitude; public final double longitude; + public final PlanetModel planetModel; public final GeoPoint[] edgePoints; - public GeoDegeneratePoint(final double lat, final double lon) { - super(lat, lon); + public GeoDegeneratePoint(final PlanetModel planetModel, final double lat, final double lon) { + super(planetModel, lat, lon); + this.planetModel = planetModel; this.latitude = lat; this.longitude = lon; this.edgePoints = new GeoPoint[]{this}; @@ -47,7 +49,7 @@ public class GeoDegeneratePoint extends GeoPoint implements GeoBBox { final double newBottomLat = latitude - angle; final double newLeftLon = longitude - angle; final double newRightLon = longitude + angle; - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon); } /** @@ -108,15 +110,14 @@ public class GeoDegeneratePoint extends GeoPoint implements GeoBBox { if (!(o instanceof GeoDegeneratePoint)) return false; GeoDegeneratePoint other = (GeoDegeneratePoint) o; - return other.latitude == latitude && other.longitude == longitude; + return super.equals(other) && other.latitude == latitude && other.longitude == longitude; } @Override public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(latitude); - result = (int) (temp ^ (temp >>> 32)); + int result = super.hashCode(); + long temp = Double.doubleToLongBits(latitude); + result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(longitude); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; @@ -124,7 +125,7 @@ public class GeoDegeneratePoint extends GeoPoint implements GeoBBox { @Override public String toString() { - return "GeoDegeneratePoint: {lat=" + latitude + "(" + latitude * 180.0 / Math.PI + "), lon=" + longitude + "(" + longitude * 180.0 / Math.PI + ")}"; + return "GeoDegeneratePoint: {planetmodel="+planetModel+", lat=" + latitude + "(" + latitude * 180.0 / Math.PI + "), lon=" + longitude + "(" + longitude * 180.0 / Math.PI + ")}"; } /** diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateVerticalLine.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateVerticalLine.java index dd0306b01ee..7e9c8b51867 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateVerticalLine.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateVerticalLine.java @@ -22,7 +22,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoDegenerateVerticalLine extends GeoBBoxBase { +public class GeoDegenerateVerticalLine extends GeoBaseBBox { public final double topLat; public final double bottomLat; public final double longitude; @@ -43,7 +43,8 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase { /** * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, longitude: {@code -PI -> PI} */ - public GeoDegenerateVerticalLine(final double topLat, final double bottomLat, final double longitude) { + public GeoDegenerateVerticalLine(final PlanetModel planetModel, final double topLat, final double bottomLat, final double longitude) { + super(planetModel); // Argument checking if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) throw new IllegalArgumentException("Top latitude out of range"); @@ -66,8 +67,8 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase { final double cosLongitude = Math.cos(longitude); // Now build the two points - this.UHC = new GeoPoint(sinTopLat, sinLongitude, cosTopLat, cosLongitude); - this.LHC = new GeoPoint(sinBottomLat, sinLongitude, cosBottomLat, cosLongitude); + this.UHC = new GeoPoint(planetModel, sinTopLat, sinLongitude, cosTopLat, cosLongitude); + this.LHC = new GeoPoint(planetModel, sinBottomLat, sinLongitude, cosBottomLat, cosLongitude); this.plane = new Plane(cosLongitude, sinLongitude); @@ -75,10 +76,10 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase { final double sinMiddleLat = Math.sin(middleLat); final double cosMiddleLat = Math.cos(middleLat); - this.centerPoint = new GeoPoint(sinMiddleLat, sinLongitude, cosMiddleLat, cosLongitude); + this.centerPoint = new GeoPoint(planetModel, sinMiddleLat, sinLongitude, cosMiddleLat, cosLongitude); - this.topPlane = new SidedPlane(centerPoint, sinTopLat); - this.bottomPlane = new SidedPlane(centerPoint, sinBottomLat); + this.topPlane = new SidedPlane(centerPoint, planetModel, sinTopLat); + this.bottomPlane = new SidedPlane(centerPoint, planetModel, sinBottomLat); this.boundingPlane = new SidedPlane(centerPoint, -sinLongitude, cosLongitude); @@ -98,7 +99,7 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon); } @Override @@ -144,7 +145,7 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase { @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { - return p.intersects(plane, notablePoints, planePoints, bounds, boundingPlane, topPlane, bottomPlane); + return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, boundingPlane, topPlane, bottomPlane); } /** @@ -187,12 +188,13 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase { if (!(o instanceof GeoDegenerateVerticalLine)) return false; GeoDegenerateVerticalLine other = (GeoDegenerateVerticalLine) o; - return other.UHC.equals(UHC) && other.LHC.equals(LHC); + return super.equals(other) && other.UHC.equals(UHC) && other.LHC.equals(LHC); } @Override public int hashCode() { - int result = UHC.hashCode(); + int result = super.hashCode(); + result = 31 * result + UHC.hashCode(); result = 31 * result + LHC.hashCode(); return result; } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLatitudeZone.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLatitudeZone.java index 132893c62cc..5bec5807bf0 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLatitudeZone.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLatitudeZone.java @@ -22,7 +22,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoLatitudeZone extends GeoBBoxBase { +public class GeoLatitudeZone extends GeoBaseBBox { public final double topLat; public final double bottomLat; public final double cosTopLat; @@ -40,7 +40,8 @@ public class GeoLatitudeZone extends GeoBBoxBase { // Edge points public final GeoPoint[] edgePoints; - public GeoLatitudeZone(final double topLat, final double bottomLat) { + public GeoLatitudeZone(final PlanetModel planetModel, final double topLat, final double bottomLat) { + super(planetModel); this.topLat = topLat; this.bottomLat = bottomLat; @@ -49,19 +50,15 @@ public class GeoLatitudeZone extends GeoBBoxBase { this.cosTopLat = Math.cos(topLat); this.cosBottomLat = Math.cos(bottomLat); - // Construct sample points, so we get our sidedness right - final Vector topPoint = new Vector(0.0, 0.0, sinTopLat); - final Vector bottomPoint = new Vector(0.0, 0.0, sinBottomLat); - // Compute an interior point. Pick one whose lat is between top and bottom. final double middleLat = (topLat + bottomLat) * 0.5; final double sinMiddleLat = Math.sin(middleLat); - this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 0.0, sinMiddleLat); - this.topBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinTopLat * sinTopLat), 0.0, sinTopLat); - this.bottomBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinBottomLat * sinBottomLat), 0.0, sinBottomLat); + this.interiorPoint = new GeoPoint(planetModel, sinMiddleLat, 0.0, Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 1.0); + this.topBoundaryPoint = new GeoPoint(planetModel, sinTopLat, 0.0, Math.sqrt(1.0 - sinTopLat * sinTopLat), 1.0); + this.bottomBoundaryPoint = new GeoPoint(planetModel, sinBottomLat, 0.0, Math.sqrt(1.0 - sinBottomLat * sinBottomLat), 1.0); - this.topPlane = new SidedPlane(interiorPoint, sinTopLat); - this.bottomPlane = new SidedPlane(interiorPoint, sinBottomLat); + this.topPlane = new SidedPlane(interiorPoint, planetModel, sinTopLat); + this.bottomPlane = new SidedPlane(interiorPoint, planetModel, sinBottomLat); this.edgePoints = new GeoPoint[]{topBoundaryPoint, bottomBoundaryPoint}; } @@ -70,7 +67,7 @@ public class GeoLatitudeZone extends GeoBBoxBase { public GeoBBox expand(final double angle) { final double newTopLat = topLat + angle; final double newBottomLat = bottomLat - angle; - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, -Math.PI, Math.PI); } @Override @@ -115,8 +112,8 @@ public class GeoLatitudeZone extends GeoBBoxBase { @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { - return p.intersects(topPlane, notablePoints, planePoints, bounds, bottomPlane) || - p.intersects(bottomPlane, notablePoints, planePoints, bounds, topPlane); + return p.intersects(planetModel, topPlane, notablePoints, planePoints, bounds, bottomPlane) || + p.intersects(planetModel, bottomPlane, notablePoints, planePoints, bounds, topPlane); } /** @@ -181,18 +178,19 @@ public class GeoLatitudeZone extends GeoBBoxBase { if (!(o instanceof GeoLatitudeZone)) return false; GeoLatitudeZone other = (GeoLatitudeZone) o; - return other.topPlane.equals(topPlane) && other.bottomPlane.equals(bottomPlane); + return super.equals(other) && other.topBoundaryPoint.equals(topBoundaryPoint) && other.bottomBoundaryPoint.equals(bottomBoundaryPoint); } @Override public int hashCode() { - int result = topPlane.hashCode(); - result = 31 * result + bottomPlane.hashCode(); + int result = super.hashCode(); + result = 31 * result + topBoundaryPoint.hashCode(); + result = 31 * result + bottomBoundaryPoint.hashCode(); return result; } @Override public String toString() { - return "GeoLatitudeZone: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + ")}"; + return "GeoLatitudeZone: {planetmodel="+planetModel+", toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLongitudeSlice.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLongitudeSlice.java index adf1fe3f123..d500b95d3ec 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLongitudeSlice.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLongitudeSlice.java @@ -24,23 +24,24 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoLongitudeSlice extends GeoBBoxBase { +public class GeoLongitudeSlice extends GeoBaseBBox { public final double leftLon; public final double rightLon; public final SidedPlane leftPlane; public final SidedPlane rightPlane; - public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE, SOUTH_POLE}; + public final GeoPoint[] planePoints; public final GeoPoint centerPoint; - public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; + public final GeoPoint[] edgePoints; /** * Accepts only values in the following ranges: lon: {@code -PI -> PI} */ - public GeoLongitudeSlice(final double leftLon, double rightLon) { + public GeoLongitudeSlice(final PlanetModel planetModel, final double leftLon, double rightLon) { + super(planetModel); // Argument checking if (leftLon < -Math.PI || leftLon > Math.PI) throw new IllegalArgumentException("Left longitude out of range"); @@ -66,11 +67,13 @@ public class GeoLongitudeSlice extends GeoBBoxBase { rightLon += Math.PI * 2.0; } final double middleLon = (leftLon + rightLon) * 0.5; - this.centerPoint = new GeoPoint(0.0, middleLon); + this.centerPoint = new GeoPoint(planetModel, 0.0, middleLon); this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); + this.planePoints = new GeoPoint[]{planetModel.NORTH_POLE, planetModel.SOUTH_POLE}; + this.edgePoints = new GeoPoint[]{planetModel.NORTH_POLE}; } @Override @@ -85,7 +88,7 @@ public class GeoLongitudeSlice extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, Math.PI * 0.5, -Math.PI * 0.5, newLeftLon, newRightLon); } @Override @@ -126,8 +129,8 @@ public class GeoLongitudeSlice extends GeoBBoxBase { @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { - return p.intersects(leftPlane, notablePoints, planePoints, bounds, rightPlane) || - p.intersects(rightPlane, notablePoints, planePoints, bounds, leftPlane); + return p.intersects(planetModel, leftPlane, notablePoints, planePoints, bounds, rightPlane) || + p.intersects(planetModel, rightPlane, notablePoints, planePoints, bounds, leftPlane); } /** @@ -154,7 +157,7 @@ public class GeoLongitudeSlice extends GeoBBoxBase { if (insideRectangle == SOME_INSIDE) return OVERLAPS; - final boolean insideShape = path.isWithin(NORTH_POLE); + final boolean insideShape = path.isWithin(planetModel.NORTH_POLE); if (insideRectangle == ALL_INSIDE && insideShape) return OVERLAPS; @@ -180,15 +183,14 @@ public class GeoLongitudeSlice extends GeoBBoxBase { if (!(o instanceof GeoLongitudeSlice)) return false; GeoLongitudeSlice other = (GeoLongitudeSlice) o; - return other.leftLon == leftLon && other.rightLon == rightLon; + return super.equals(other) && other.leftLon == leftLon && other.rightLon == rightLon; } @Override public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(leftLon); - result = (int) (temp ^ (temp >>> 32)); + int result = super.hashCode(); + long temp = Double.doubleToLongBits(leftLon); + result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(rightLon); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; @@ -196,7 +198,7 @@ public class GeoLongitudeSlice extends GeoBBoxBase { @Override public String toString() { - return "GeoLongitudeSlice: {leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + return "GeoLongitudeSlice: {planetmodel="+planetModel+", leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthLatitudeZone.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthLatitudeZone.java index 4a03be2fd74..1a2c1281fbb 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthLatitudeZone.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthLatitudeZone.java @@ -22,7 +22,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoNorthLatitudeZone extends GeoBBoxBase { +public class GeoNorthLatitudeZone extends GeoBaseBBox { public final double bottomLat; public final double cosBottomLat; public final SidedPlane bottomPlane; @@ -34,22 +34,20 @@ public class GeoNorthLatitudeZone extends GeoBBoxBase { // Edge points public final GeoPoint[] edgePoints; - public GeoNorthLatitudeZone(final double bottomLat) { + public GeoNorthLatitudeZone(final PlanetModel planetModel, final double bottomLat) { + super(planetModel); this.bottomLat = bottomLat; final double sinBottomLat = Math.sin(bottomLat); this.cosBottomLat = Math.cos(bottomLat); - // Construct sample points, so we get our sidedness right - final Vector bottomPoint = new Vector(0.0, 0.0, sinBottomLat); - // Compute an interior point. Pick one whose lat is between top and bottom. final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5; final double sinMiddleLat = Math.sin(middleLat); - this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 0.0, sinMiddleLat); - this.bottomBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinBottomLat * sinBottomLat), 0.0, sinBottomLat); + this.interiorPoint = new GeoPoint(planetModel, sinMiddleLat, 0.0, Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 1.0); + this.bottomBoundaryPoint = new GeoPoint(planetModel, sinBottomLat, 0.0, Math.sqrt(1.0 - sinBottomLat * sinBottomLat), 1.0); - this.bottomPlane = new SidedPlane(interiorPoint, sinBottomLat); + this.bottomPlane = new SidedPlane(interiorPoint, planetModel, sinBottomLat); this.edgePoints = new GeoPoint[]{bottomBoundaryPoint}; } @@ -58,7 +56,7 @@ public class GeoNorthLatitudeZone extends GeoBBoxBase { public GeoBBox expand(final double angle) { final double newTopLat = Math.PI * 0.5; final double newBottomLat = bottomLat - angle; - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, -Math.PI, Math.PI); } @Override @@ -101,7 +99,7 @@ public class GeoNorthLatitudeZone extends GeoBBoxBase { @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { return - p.intersects(bottomPlane, notablePoints, planePoints, bounds); + p.intersects(planetModel, bottomPlane, notablePoints, planePoints, bounds); } /** @@ -159,18 +157,19 @@ public class GeoNorthLatitudeZone extends GeoBBoxBase { if (!(o instanceof GeoNorthLatitudeZone)) return false; GeoNorthLatitudeZone other = (GeoNorthLatitudeZone) o; - return other.bottomPlane.equals(bottomPlane); + return super.equals(other) && other.bottomBoundaryPoint.equals(bottomBoundaryPoint); } @Override public int hashCode() { - int result = bottomPlane.hashCode(); + int result = super.hashCode(); + result = 31 * result + bottomBoundaryPoint.hashCode(); return result; } @Override public String toString() { - return "GeoNorthLatitudeZone: {bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + ")}"; + return "GeoNorthLatitudeZone: {planetmodel="+planetModel+", bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthRectangle.java index be0c1cbcec2..32f0a2e3498 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthRectangle.java @@ -25,7 +25,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoNorthRectangle extends GeoBBoxBase { +public class GeoNorthRectangle extends GeoBaseBBox { public final double bottomLat; public final double leftLon; public final double rightLon; @@ -45,12 +45,13 @@ public class GeoNorthRectangle extends GeoBBoxBase { public final GeoPoint centerPoint; - public final GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; + public final GeoPoint[] edgePoints; /** * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} */ - public GeoNorthRectangle(final double bottomLat, final double leftLon, double rightLon) { + public GeoNorthRectangle(final PlanetModel planetModel, final double bottomLat, final double leftLon, double rightLon) { + super(planetModel); // Argument checking if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) throw new IllegalArgumentException("Bottom latitude out of range"); @@ -77,8 +78,8 @@ public class GeoNorthRectangle extends GeoBBoxBase { final double cosRightLon = Math.cos(rightLon); // Now build the points - this.LRHC = new GeoPoint(sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); - this.LLHC = new GeoPoint(sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); + this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); + this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5; final double sinMiddleLat = Math.sin(middleLat); @@ -91,16 +92,17 @@ public class GeoNorthRectangle extends GeoBBoxBase { final double sinMiddleLon = Math.sin(middleLon); final double cosMiddleLon = Math.cos(middleLon); - this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + this.centerPoint = new GeoPoint(planetModel, sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); - this.bottomPlane = new SidedPlane(centerPoint, sinBottomLat); + this.bottomPlane = new SidedPlane(centerPoint, planetModel, sinBottomLat); this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); this.bottomPlanePoints = new GeoPoint[]{LLHC, LRHC}; - this.leftPlanePoints = new GeoPoint[]{NORTH_POLE, LLHC}; - this.rightPlanePoints = new GeoPoint[]{NORTH_POLE, LRHC}; + this.leftPlanePoints = new GeoPoint[]{planetModel.NORTH_POLE, LLHC}; + this.rightPlanePoints = new GeoPoint[]{planetModel.NORTH_POLE, LRHC}; + this.edgePoints = new GeoPoint[]{planetModel.NORTH_POLE}; } @Override @@ -117,7 +119,7 @@ public class GeoNorthRectangle extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon); } @Override @@ -164,9 +166,9 @@ public class GeoNorthRectangle extends GeoBBoxBase { @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { return - p.intersects(bottomPlane, notablePoints, bottomPlanePoints, bounds, leftPlane, rightPlane) || - p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, rightPlane, bottomPlane) || - p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, bottomPlane); + p.intersects(planetModel, bottomPlane, notablePoints, bottomPlanePoints, bounds, leftPlane, rightPlane) || + p.intersects(planetModel, leftPlane, notablePoints, leftPlanePoints, bounds, rightPlane, bottomPlane) || + p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, bottomPlane); } /** @@ -196,7 +198,7 @@ public class GeoNorthRectangle extends GeoBBoxBase { return OVERLAPS; } - final boolean insideShape = path.isWithin(NORTH_POLE); + final boolean insideShape = path.isWithin(planetModel.NORTH_POLE); if (insideRectangle == ALL_INSIDE && insideShape) { //System.err.println(" inside of each other"); @@ -229,19 +231,20 @@ public class GeoNorthRectangle extends GeoBBoxBase { if (!(o instanceof GeoNorthRectangle)) return false; GeoNorthRectangle other = (GeoNorthRectangle) o; - return other.LLHC.equals(LLHC) && other.LRHC.equals(LRHC); + return super.equals(other) && other.LLHC.equals(LLHC) && other.LRHC.equals(LRHC); } @Override public int hashCode() { - int result = LLHC.hashCode(); + int result = super.hashCode(); + result = 31 * result + LLHC.hashCode(); result = 31 * result + LRHC.hashCode(); return result; } @Override public String toString() { - return "GeoNorthRectangle: {bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + return "GeoNorthRectangle: {planetmodel="+planetModel+", bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java index 2ff08b05f0a..379467b0483 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java @@ -29,29 +29,25 @@ import java.util.List; * @lucene.experimental */ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { + public final double cutoffAngle; - public final double cutoffOffset; - public final double originDistance; - public final double chordDistance; + public final double sinAngle; + public final double cosAngle; - public final List points = new ArrayList(); + public final List points = new ArrayList(); + + public final List endPoints = new ArrayList(); public final List segments = new ArrayList(); public GeoPoint[] edgePoints = null; - public GeoPath(final double cutoffAngle) { - super(); - if (cutoffAngle <= 0.0 || cutoffAngle > Math.PI * 0.5) + public GeoPath(final PlanetModel planetModel, final double maxCutoffAngle) { + super(planetModel); + if (maxCutoffAngle <= 0.0 || maxCutoffAngle > Math.PI * 0.5) throw new IllegalArgumentException("Cutoff angle out of bounds"); - this.cutoffAngle = cutoffAngle; - final double cosAngle = Math.cos(cutoffAngle); - final double sinAngle = Math.sin(cutoffAngle); - // Cutoff offset is the linear distance given the angle - this.cutoffOffset = sinAngle; - this.originDistance = cosAngle; - // Compute chord distance - double xDiff = 1.0 - cosAngle; - this.chordDistance = Math.sqrt(xDiff * xDiff + sinAngle * sinAngle); + this.cutoffAngle = maxCutoffAngle; + this.cosAngle = Math.cos(maxCutoffAngle); + this.sinAngle = Math.sin(maxCutoffAngle); } public void addPoint(double lat, double lon) { @@ -59,57 +55,80 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { throw new IllegalArgumentException("Latitude out of range"); if (lon < -Math.PI || lon > Math.PI) throw new IllegalArgumentException("Longitude out of range"); - final GeoPoint end = new GeoPoint(lat, lon); - if (points.size() > 0) { - final GeoPoint start = points.get(points.size() - 1).point; - final PathSegment ps = new PathSegment(start, end, cutoffOffset, cutoffAngle, chordDistance); - // Check for degeneracy; if the segment is degenerate, don't include the point - if (ps.isDegenerate()) - return; - segments.add(ps); - } else { - // First point. We compute the basic set of edgepoints here because we've got the lat and lon available. - // Move from center only in latitude. Then, if we go past the north pole, adjust the longitude also. - double newLat = lat + cutoffAngle; - double newLon = lon; - if (newLat > Math.PI * 0.5) { - newLat = Math.PI - newLat; - newLon += Math.PI; - } - while (newLon > Math.PI) { - newLon -= Math.PI * 2.0; - } - final GeoPoint edgePoint = new GeoPoint(newLat, newLon); - this.edgePoints = new GeoPoint[]{edgePoint}; - } - final SegmentEndpoint se = new SegmentEndpoint(end, originDistance, cutoffOffset, cutoffAngle, chordDistance); - points.add(se); + points.add(new GeoPoint(planetModel, lat, lon)); } - + public void done() { if (points.size() == 0) throw new IllegalArgumentException("Path must have at least one point"); - if (segments.size() > 0) { - edgePoints = new GeoPoint[]{points.get(0).circlePlane.getSampleIntersectionPoint(segments.get(0).invertedStartCutoffPlane)}; - } - for (int i = 0; i < points.size(); i++) { - final SegmentEndpoint pathPoint = points.get(i); - Membership previousEndBound = null; - GeoPoint[] previousEndNotablePoints = null; - Membership nextStartBound = null; - GeoPoint[] nextStartNotablePoints = null; - if (i > 0) { - final PathSegment previousSegment = segments.get(i - 1); - previousEndBound = previousSegment.invertedEndCutoffPlane; - previousEndNotablePoints = previousSegment.endCutoffPlanePoints; + // Compute an offset to use for all segments. This will be based on the minimum magnitude of + // the entire ellipsoid. + final double cutoffOffset = this.sinAngle * planetModel.getMinimumMagnitude(); + + // First, build all segments. We'll then go back and build corresponding segment endpoints. + GeoPoint lastPoint = null; + for (final GeoPoint end : points) { + if (lastPoint != null) { + final Plane normalizedConnectingPlane = new Plane(lastPoint, end).normalize(); + if (normalizedConnectingPlane == null) { + continue; + } + segments.add(new PathSegment(planetModel, lastPoint, end, normalizedConnectingPlane, cutoffOffset)); } - if (i < segments.size()) { - final PathSegment nextSegment = segments.get(i); - nextStartBound = nextSegment.invertedStartCutoffPlane; - nextStartNotablePoints = nextSegment.startCutoffPlanePoints; - } - pathPoint.setCutoffPlanes(previousEndNotablePoints, previousEndBound, nextStartNotablePoints, nextStartBound); + lastPoint = end; } + + if (segments.size() == 0) { + // Simple circle + final SegmentEndpoint onlyEndpoint = new SegmentEndpoint(points.get(0), cutoffOffset); + endPoints.add(onlyEndpoint); + // Find an edgepoint + // We already have circle plane, which is the definitive determination of the edge of the "circle". + // Next, compute vertical plane going through origin and the center point (C = 0, D = 0). + Plane verticalPlane = Plane.constructNormalizedVerticalPlane(onlyEndpoint.point.x, onlyEndpoint.point.y); + if (verticalPlane == null) { + verticalPlane = new Plane(1.0,0.0); + } + // Finally, use Plane.findIntersections() to find the intersection points. + final GeoPoint edgePoint = onlyEndpoint.circlePlane.getSampleIntersectionPoint(planetModel, verticalPlane); + if (edgePoint == null) { + throw new RuntimeException("Could not find edge point for path endpoint="+onlyEndpoint.point+" cutoffOffset="+cutoffOffset+" planetModel="+planetModel); + } + this.edgePoints = new GeoPoint[]{edgePoint}; + return; + } + + // Create segment endpoints. Use an appropriate constructor for the start and end of the path. + for (int i = 0; i < segments.size(); i++) { + final PathSegment currentSegment = segments.get(i); + + if (i == 0) { + // Starting endpoint + final SegmentEndpoint startEndpoint = new SegmentEndpoint(currentSegment.start, + currentSegment.startCutoffPlane, currentSegment.ULHC, currentSegment.LLHC); + endPoints.add(startEndpoint); + this.edgePoints = new GeoPoint[]{currentSegment.ULHC}; + continue; + } + + // General intersection case + final PathSegment prevSegment = segments.get(i-1); + if (prevSegment.upperConnectingPlane.isNumericallyIdentical(currentSegment.upperConnectingPlane) && + prevSegment.lowerConnectingPlane.isNumericallyIdentical(currentSegment.lowerConnectingPlane)) { + // The planes are identical. We don't need a circle at all. Special constructor... + endPoints.add(new SegmentEndpoint(currentSegment.start)); + } else { + endPoints.add(new SegmentEndpoint(currentSegment.start, + prevSegment.endCutoffPlane, currentSegment.startCutoffPlane, + prevSegment.URHC, prevSegment.LRHC, + currentSegment.ULHC, currentSegment.LLHC)); + } + } + // Do final endpoint + final PathSegment lastSegment = segments.get(segments.size()-1); + endPoints.add(new SegmentEndpoint(lastSegment.end, + lastSegment.endCutoffPlane, lastSegment.URHC, lastSegment.LRHC)); + } /** @@ -132,7 +151,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { int segmentIndex = 0; currentDistance = 0.0; - for (SegmentEndpoint endpoint : points) { + for (SegmentEndpoint endpoint : endPoints) { double distance = endpoint.pathNormalDistance(point); if (distance != Double.MAX_VALUE) return currentDistance + distance; @@ -194,7 +213,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { int segmentIndex = 0; currentDistance = 0.0; - for (SegmentEndpoint endpoint : points) { + for (SegmentEndpoint endpoint : endPoints) { double distance = endpoint.pathLinearDistance(point); if (distance != Double.MAX_VALUE) return currentDistance + distance; @@ -251,7 +270,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { int segmentIndex = 0; currentDistance = 0.0; - for (SegmentEndpoint endpoint : points) { + for (SegmentEndpoint endpoint : endPoints) { double distance = endpoint.pathDistance(point); if (distance != Double.MAX_VALUE) return currentDistance + distance; @@ -264,20 +283,26 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { @Override public boolean isWithin(final Vector point) { - for (SegmentEndpoint pathPoint : points) { - if (pathPoint.isWithin(point)) + //System.err.println("Assessing whether point "+point+" is within geopath "+this); + for (SegmentEndpoint pathPoint : endPoints) { + if (pathPoint.isWithin(point)) { + //System.err.println(" point is within SegmentEndpoint "+pathPoint); return true; + } } for (PathSegment pathSegment : segments) { - if (pathSegment.isWithin(point)) + if (pathSegment.isWithin(point)) { + //System.err.println(" point is within PathSegment "+pathSegment); return true; + } } + //System.err.println(" point is not within geopath"); return false; } @Override public boolean isWithin(final double x, final double y, final double z) { - for (SegmentEndpoint pathPoint : points) { + for (SegmentEndpoint pathPoint : endPoints) { if (pathPoint.isWithin(x, y, z)) return true; } @@ -304,15 +329,15 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { // any of the intersection points are within the bounds, then we've detected an intersection. // Well, sort of. We can detect intersections also due to overlap of segments with each other. // But that's an edge case and we won't be optimizing for it. - - for (final SegmentEndpoint pathPoint : points) { - if (pathPoint.intersects(plane, notablePoints, bounds)) { + //System.err.println(" Looking for intersection of plane "+plane+" with path "+this); + for (final SegmentEndpoint pathPoint : endPoints) { + if (pathPoint.intersects(planetModel, plane, notablePoints, bounds)) { return true; } } for (final PathSegment pathSegment : segments) { - if (pathSegment.intersects(plane, notablePoints, bounds)) { + if (pathSegment.intersects(planetModel, plane, notablePoints, bounds)) { return true; } } @@ -336,10 +361,10 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { // never more than 180 degrees longitude at a pop or we risk having the // bounds object get itself inverted. So do the edges first. for (PathSegment pathSegment : segments) { - pathSegment.getBounds(bounds); + pathSegment.getBounds(planetModel, bounds); } - for (SegmentEndpoint pathPoint : points) { - pathPoint.getBounds(bounds); + for (SegmentEndpoint pathPoint : endPoints) { + pathPoint.getBounds(planetModel, bounds); } return bounds; } @@ -349,13 +374,15 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { if (!(o instanceof GeoPath)) return false; GeoPath p = (GeoPath) o; - if (points.size() != p.points.size()) + if (!super.equals(p)) + return false; + if (endPoints.size() != p.endPoints.size()) return false; if (cutoffAngle != p.cutoffAngle) return false; - for (int i = 0; i < points.size(); i++) { - SegmentEndpoint point = points.get(i); - SegmentEndpoint point2 = p.points.get(i); + for (int i = 0; i < endPoints.size(); i++) { + SegmentEndpoint point = endPoints.get(i); + SegmentEndpoint point2 = p.endPoints.get(i); if (!point.equals(point2)) return false; } @@ -364,101 +391,168 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { @Override public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(cutoffAngle); - result = (int) (temp ^ (temp >>> 32)); - result = 31 * result + points.hashCode(); + int result = super.hashCode(); + long temp = Double.doubleToLongBits(cutoffAngle); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + endPoints.hashCode(); return result; } @Override public String toString() { - return "GeoPath: {width=" + cutoffAngle + "(" + cutoffAngle * 180.0 / Math.PI + "), points={" + points + "}}"; + return "GeoPath: {planetmodel=" + planetModel+", width=" + cutoffAngle + "(" + cutoffAngle * 180.0 / Math.PI + "), points={" + points + "}}"; } /** * This is precalculated data for segment endpoint. + * Note well: This is not necessarily a circle. There are four cases: + * (1) The path consists of a single endpoint. In this case, we build a simple circle with the proper cutoff offset. + * (2) This is the end of a path. The circle plane must be constructed to go through two supplied points and be perpendicular to a connecting plane. + * (3) This is an intersection in a path. We are supplied FOUR planes. If there are intersections within bounds for both upper and lower, then + * we generate no circle at all. If there is one intersection only, then we generate a plane that includes that intersection, as well as the remaining + * cutoff plane/edge plane points. */ public static class SegmentEndpoint { public final GeoPoint point; public final SidedPlane circlePlane; - public final double cutoffNormalDistance; - public final double cutoffAngle; - public final double chordDistance; - public Membership[] cutoffPlanes = null; - public GeoPoint[] notablePoints = null; + public final Membership[] cutoffPlanes; + public final GeoPoint[] notablePoints; public final static GeoPoint[] circlePoints = new GeoPoint[0]; - public SegmentEndpoint(final GeoPoint point, final double originDistance, final double cutoffOffset, final double cutoffAngle, final double chordDistance) { + /** Base case. Does nothing at all. + */ + public SegmentEndpoint(final GeoPoint point) { this.point = point; - this.cutoffNormalDistance = cutoffOffset; - this.cutoffAngle = cutoffAngle; - this.chordDistance = chordDistance; - this.circlePlane = new SidedPlane(point, point, -originDistance); + this.circlePlane = null; + this.cutoffPlanes = null; + this.notablePoints = null; } + + /** Constructor for case (1). + * Generate a simple circle cutoff plane. + */ + public SegmentEndpoint(final GeoPoint point, final double cutoffOffset) { + this.point = point; + final double magnitude = point.magnitude(); + // Normalize vector to make D value correct + this.circlePlane = new SidedPlane(point, point.normalize(), -Math.sqrt(magnitude * magnitude - cutoffOffset * cutoffOffset)); + this.cutoffPlanes = new Membership[0]; + this.notablePoints = new GeoPoint[0]; + } + + /** Constructor for case (2). + * Generate an endpoint, given a single cutoff plane plus upper and lower edge points. + */ + public SegmentEndpoint(final GeoPoint point, + final SidedPlane cutoffPlane, final GeoPoint topEdgePoint, final GeoPoint bottomEdgePoint) { + this.point = point; + this.cutoffPlanes = new Membership[]{new SidedPlane(cutoffPlane)}; + this.notablePoints = new GeoPoint[]{topEdgePoint, bottomEdgePoint}; + // To construct the plane, we now just need D, which is simply the negative of the evaluation of the circle normal vector at one of the points. + this.circlePlane = SidedPlane.constructNormalizedPerpendicularSidedPlane(point, cutoffPlane, topEdgePoint, bottomEdgePoint); + } + + /** Constructor for case (3). + * Generate an endpoint for an intersection, given four points. + */ + public SegmentEndpoint(final GeoPoint point, + final SidedPlane prevCutoffPlane, final SidedPlane nextCutoffPlane, + final GeoPoint prevUpperGeoPoint, final GeoPoint prevLowerGeoPoint, + final GeoPoint nextUpperGeoPoint, final GeoPoint nextLowerGeoPoint) { + // Note: What we really need is a single plane that goes through all four points. + // Since that's not possible in the ellipsoid case (because three points determine a plane, not four), we + // need an approximation that at least creates a boundary that has no interruptions. + // There are three obvious choices for the third point: either (a) one of the two remaining points, or (b) the top or bottom edge + // intersection point. (a) has no guarantee of continuity, while (b) is capable of producing something very far from a circle if + // the angle between segments is acute. + // The solution is to look for the side (top or bottom) that has an intersection within the shape. We use the two points from + // the opposite side to determine the plane, AND we pick the third to be either of the two points on the intersecting side + // PROVIDED that the other point is within the final circle we come up with. + this.point = point; + + // We construct four separate planes, and evaluate which one includes all interior points with least overlap + final SidedPlane candidate1 = SidedPlane.constructNormalizedThreePointSidedPlane(point, prevUpperGeoPoint, nextUpperGeoPoint, nextLowerGeoPoint); + final SidedPlane candidate2 = SidedPlane.constructNormalizedThreePointSidedPlane(point, nextUpperGeoPoint, nextLowerGeoPoint, prevLowerGeoPoint); + final SidedPlane candidate3 = SidedPlane.constructNormalizedThreePointSidedPlane(point, nextLowerGeoPoint, prevLowerGeoPoint, prevUpperGeoPoint); + final SidedPlane candidate4 = SidedPlane.constructNormalizedThreePointSidedPlane(point, prevLowerGeoPoint, prevUpperGeoPoint, nextUpperGeoPoint); - public void setCutoffPlanes(final GeoPoint[] previousEndNotablePoints, final Membership previousEndPlane, - final GeoPoint[] nextStartNotablePoints, final Membership nextStartPlane) { - if (previousEndNotablePoints == null && nextStartNotablePoints == null) { - cutoffPlanes = new Membership[0]; - notablePoints = new GeoPoint[0]; - } else if (previousEndNotablePoints != null && nextStartNotablePoints == null) { - cutoffPlanes = new Membership[]{previousEndPlane}; - notablePoints = previousEndNotablePoints; - } else if (previousEndNotablePoints == null && nextStartNotablePoints != null) { - cutoffPlanes = new Membership[]{nextStartPlane}; - notablePoints = nextStartNotablePoints; + final boolean cand1IsOtherWithin = candidate1.isWithin(prevLowerGeoPoint); + final boolean cand2IsOtherWithin = candidate2.isWithin(prevUpperGeoPoint); + final boolean cand3IsOtherWithin = candidate3.isWithin(nextUpperGeoPoint); + final boolean cand4IsOtherWithin = candidate4.isWithin(nextLowerGeoPoint); + + if (cand1IsOtherWithin && cand2IsOtherWithin && cand3IsOtherWithin && cand4IsOtherWithin) { + // The only way we should see both within is if all four points are coplanar. In that case, we default to the simplest treatment. + this.circlePlane = candidate1; // doesn't matter which + this.notablePoints = new GeoPoint[]{prevUpperGeoPoint, nextUpperGeoPoint, prevLowerGeoPoint, nextLowerGeoPoint}; + this.cutoffPlanes = new Membership[]{new SidedPlane(prevCutoffPlane), new SidedPlane(nextCutoffPlane)}; + } else if (cand1IsOtherWithin) { + // Use candidate1, and DON'T include prevCutoffPlane in the cutoff planes list + this.circlePlane = candidate1; + this.notablePoints = new GeoPoint[]{prevUpperGeoPoint, nextUpperGeoPoint, nextLowerGeoPoint}; + this.cutoffPlanes = new Membership[]{new SidedPlane(nextCutoffPlane)}; + } else if (cand2IsOtherWithin) { + // Use candidate2 + this.circlePlane = candidate2; + this.notablePoints = new GeoPoint[]{nextUpperGeoPoint, nextLowerGeoPoint, prevLowerGeoPoint}; + this.cutoffPlanes = new Membership[]{new SidedPlane(nextCutoffPlane)}; + } else if (cand3IsOtherWithin) { + this.circlePlane = candidate3; + this.notablePoints = new GeoPoint[]{nextLowerGeoPoint, prevLowerGeoPoint, prevUpperGeoPoint}; + this.cutoffPlanes = new Membership[]{new SidedPlane(prevCutoffPlane)}; + } else if (cand4IsOtherWithin) { + this.circlePlane = candidate4; + this.notablePoints = new GeoPoint[]{prevLowerGeoPoint, prevUpperGeoPoint, nextUpperGeoPoint}; + this.cutoffPlanes = new Membership[]{new SidedPlane(prevCutoffPlane)}; } else { - cutoffPlanes = new Membership[]{previousEndPlane, nextStartPlane}; - notablePoints = new GeoPoint[previousEndNotablePoints.length + nextStartNotablePoints.length]; - int i = 0; - for (GeoPoint p : previousEndNotablePoints) { - notablePoints[i++] = p; - } - for (GeoPoint p : nextStartNotablePoints) { - notablePoints[i++] = p; - } + // dunno what happened + throw new RuntimeException("Couldn't come up with a plane through three points that included the fourth"); } } public boolean isWithin(final Vector point) { + if (circlePlane == null) + return false; return circlePlane.isWithin(point); } public boolean isWithin(final double x, final double y, final double z) { + if (circlePlane == null) + return false; return circlePlane.isWithin(x, y, z); } public double pathDistance(final GeoPoint point) { - double dist = this.point.arcDistance(point); - if (dist > cutoffAngle) + if (!isWithin(point)) return Double.MAX_VALUE; - return dist; + return this.point.arcDistance(point); } public double pathNormalDistance(final GeoPoint point) { - double dist = this.point.normalDistance(point); - if (dist > cutoffNormalDistance) + if (!isWithin(point)) return Double.MAX_VALUE; - return dist; + return this.point.normalDistance(point); } public double pathLinearDistance(final GeoPoint point) { - double dist = this.point.linearDistance(point); - if (dist > chordDistance) + if (!isWithin(point)) return Double.MAX_VALUE; - return dist; + return this.point.linearDistance(point); } - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) { - return circlePlane.intersects(p, notablePoints, this.notablePoints, bounds, this.cutoffPlanes); + public boolean intersects(final PlanetModel planetModel, final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) { + //System.err.println(" looking for intersection between plane "+p+" and circle "+circlePlane+" on proper side of "+cutoffPlanes+" within "+bounds); + if (circlePlane == null) + return false; + return circlePlane.intersects(planetModel, p, notablePoints, this.notablePoints, bounds, this.cutoffPlanes); } - public void getBounds(Bounds bounds) { + public void getBounds(final PlanetModel planetModel, Bounds bounds) { bounds.addPoint(point); - circlePlane.recordBounds(bounds); + if (circlePlane == null) + return; + circlePlane.recordBounds(planetModel, bounds); } @Override @@ -494,69 +588,70 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { public final SidedPlane lowerConnectingPlane; public final SidedPlane startCutoffPlane; public final SidedPlane endCutoffPlane; + public final GeoPoint URHC; + public final GeoPoint LRHC; + public final GeoPoint ULHC; + public final GeoPoint LLHC; public final GeoPoint[] upperConnectingPlanePoints; public final GeoPoint[] lowerConnectingPlanePoints; public final GeoPoint[] startCutoffPlanePoints; public final GeoPoint[] endCutoffPlanePoints; public final double planeBoundingOffset; - public final double arcWidth; - public final double chordDistance; - // For the adjoining SegmentEndpoint... - public final SidedPlane invertedStartCutoffPlane; - public final SidedPlane invertedEndCutoffPlane; - - public PathSegment(final GeoPoint start, final GeoPoint end, final double planeBoundingOffset, final double arcWidth, final double chordDistance) { + public PathSegment(final PlanetModel planetModel, final GeoPoint start, final GeoPoint end, + final Plane normalizedConnectingPlane, final double planeBoundingOffset) { this.start = start; this.end = end; + this.normalizedConnectingPlane = normalizedConnectingPlane; this.planeBoundingOffset = planeBoundingOffset; - this.arcWidth = arcWidth; - this.chordDistance = chordDistance; fullDistance = start.arcDistance(end); fullNormalDistance = start.normalDistance(end); fullLinearDistance = start.linearDistance(end); - normalizedConnectingPlane = new Plane(start, end).normalize(); - if (normalizedConnectingPlane == null) { - upperConnectingPlane = null; - lowerConnectingPlane = null; - startCutoffPlane = null; - endCutoffPlane = null; - upperConnectingPlanePoints = null; - lowerConnectingPlanePoints = null; - startCutoffPlanePoints = null; - endCutoffPlanePoints = null; - invertedStartCutoffPlane = null; - invertedEndCutoffPlane = null; - } else { - // Either start or end should be on the correct side - upperConnectingPlane = new SidedPlane(start, normalizedConnectingPlane, -planeBoundingOffset); - lowerConnectingPlane = new SidedPlane(start, normalizedConnectingPlane, planeBoundingOffset); - // Cutoff planes use opposite endpoints as correct side examples - startCutoffPlane = new SidedPlane(end, normalizedConnectingPlane, start); - endCutoffPlane = new SidedPlane(start, normalizedConnectingPlane, end); - final Membership[] upperSide = new Membership[]{upperConnectingPlane}; - final Membership[] lowerSide = new Membership[]{lowerConnectingPlane}; - final Membership[] startSide = new Membership[]{startCutoffPlane}; - final Membership[] endSide = new Membership[]{endCutoffPlane}; - final GeoPoint ULHC = upperConnectingPlane.findIntersections(startCutoffPlane, lowerSide, endSide)[0]; - final GeoPoint URHC = upperConnectingPlane.findIntersections(endCutoffPlane, lowerSide, startSide)[0]; - final GeoPoint LLHC = lowerConnectingPlane.findIntersections(startCutoffPlane, upperSide, endSide)[0]; - final GeoPoint LRHC = lowerConnectingPlane.findIntersections(endCutoffPlane, upperSide, startSide)[0]; - upperConnectingPlanePoints = new GeoPoint[]{ULHC, URHC}; - lowerConnectingPlanePoints = new GeoPoint[]{LLHC, LRHC}; - startCutoffPlanePoints = new GeoPoint[]{ULHC, LLHC}; - endCutoffPlanePoints = new GeoPoint[]{URHC, LRHC}; - invertedStartCutoffPlane = new SidedPlane(startCutoffPlane); - invertedEndCutoffPlane = new SidedPlane(endCutoffPlane); + // Either start or end should be on the correct side + upperConnectingPlane = new SidedPlane(start, normalizedConnectingPlane, -planeBoundingOffset); + lowerConnectingPlane = new SidedPlane(start, normalizedConnectingPlane, planeBoundingOffset); + // Cutoff planes use opposite endpoints as correct side examples + startCutoffPlane = new SidedPlane(end, normalizedConnectingPlane, start); + endCutoffPlane = new SidedPlane(start, normalizedConnectingPlane, end); + final Membership[] upperSide = new Membership[]{upperConnectingPlane}; + final Membership[] lowerSide = new Membership[]{lowerConnectingPlane}; + final Membership[] startSide = new Membership[]{startCutoffPlane}; + final Membership[] endSide = new Membership[]{endCutoffPlane}; + GeoPoint[] points; + points = upperConnectingPlane.findIntersections(planetModel, startCutoffPlane, lowerSide, endSide); + if (points.length == 0) { + throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide"); } - } - - public boolean isDegenerate() { - return normalizedConnectingPlane == null; + this.ULHC = points[0]; + points = upperConnectingPlane.findIntersections(planetModel, endCutoffPlane, lowerSide, startSide); + if (points.length == 0) { + throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide"); + } + this.URHC = points[0]; + points = lowerConnectingPlane.findIntersections(planetModel, startCutoffPlane, upperSide, endSide); + if (points.length == 0) { + throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide"); + } + this.LLHC = points[0]; + points = lowerConnectingPlane.findIntersections(planetModel, endCutoffPlane, upperSide, startSide); + if (points.length == 0) { + throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide"); + } + this.LRHC = points[0]; + upperConnectingPlanePoints = new GeoPoint[]{ULHC, URHC}; + lowerConnectingPlanePoints = new GeoPoint[]{LLHC, LRHC}; + startCutoffPlanePoints = new GeoPoint[]{ULHC, LLHC}; + endCutoffPlanePoints = new GeoPoint[]{URHC, LRHC}; } public boolean isWithin(final Vector point) { + //System.err.println(" assessing whether point "+point+" is within path segment "+this); + //System.err.println(" within "+startCutoffPlane+": "+startCutoffPlane.isWithin(point)); + //System.err.println(" within "+endCutoffPlane+": "+endCutoffPlane.isWithin(point)); + //System.err.println(" within "+upperConnectingPlane+": "+upperConnectingPlane.isWithin(point)); + //System.err.println(" within "+lowerConnectingPlane+": "+lowerConnectingPlane.isWithin(point)); + return startCutoffPlane.isWithin(point) && endCutoffPlane.isWithin(point) && upperConnectingPlane.isWithin(point) && @@ -640,22 +735,22 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { return point.linearDistance(normLineX, normLineY, normLineZ) + start.linearDistance(normLineX, normLineY, normLineZ); } - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) { - return upperConnectingPlane.intersects(p, notablePoints, upperConnectingPlanePoints, bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane) || - lowerConnectingPlane.intersects(p, notablePoints, lowerConnectingPlanePoints, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane); + public boolean intersects(final PlanetModel planetModel, final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) { + return upperConnectingPlane.intersects(planetModel, p, notablePoints, upperConnectingPlanePoints, bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane) || + lowerConnectingPlane.intersects(planetModel, p, notablePoints, lowerConnectingPlanePoints, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane); } - public void getBounds(Bounds bounds) { + public void getBounds(final PlanetModel planetModel, Bounds bounds) { // We need to do all bounding planes as well as corner points bounds.addPoint(start).addPoint(end); - upperConnectingPlane.recordBounds(startCutoffPlane, bounds, lowerConnectingPlane, endCutoffPlane); - startCutoffPlane.recordBounds(lowerConnectingPlane, bounds, endCutoffPlane, upperConnectingPlane); - lowerConnectingPlane.recordBounds(endCutoffPlane, bounds, upperConnectingPlane, startCutoffPlane); - endCutoffPlane.recordBounds(upperConnectingPlane, bounds, startCutoffPlane, lowerConnectingPlane); - upperConnectingPlane.recordBounds(bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane); - lowerConnectingPlane.recordBounds(bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane); - startCutoffPlane.recordBounds(bounds, endCutoffPlane, upperConnectingPlane, lowerConnectingPlane); - endCutoffPlane.recordBounds(bounds, startCutoffPlane, upperConnectingPlane, lowerConnectingPlane); + upperConnectingPlane.recordBounds(planetModel, startCutoffPlane, bounds, lowerConnectingPlane, endCutoffPlane); + startCutoffPlane.recordBounds(planetModel, lowerConnectingPlane, bounds, endCutoffPlane, upperConnectingPlane); + lowerConnectingPlane.recordBounds(planetModel, endCutoffPlane, bounds, upperConnectingPlane, startCutoffPlane); + endCutoffPlane.recordBounds(planetModel, upperConnectingPlane, bounds, startCutoffPlane, lowerConnectingPlane); + upperConnectingPlane.recordBounds(planetModel, bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane); + lowerConnectingPlane.recordBounds(planetModel, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane); + startCutoffPlane.recordBounds(planetModel, bounds, endCutoffPlane, upperConnectingPlane, lowerConnectingPlane); + endCutoffPlane.recordBounds(planetModel, bounds, startCutoffPlane, upperConnectingPlane, lowerConnectingPlane); if (fullDistance >= Math.PI) { // Too large a segment basically means that we can confuse the Bounds object. Specifically, if our span exceeds 180 degrees // in longitude (which even a segment whose actual length is less than that might if it goes close to a pole). diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java index 93c0c847775..67844371359 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java @@ -23,20 +23,36 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * @lucene.experimental */ public class GeoPoint extends Vector { - public GeoPoint(final double sinLat, final double sinLon, final double cosLat, final double cosLon) { - super(cosLat * cosLon, cosLat * sinLon, sinLat); + + protected double magnitude = Double.NEGATIVE_INFINITY; + + public GeoPoint(final PlanetModel planetModel, final double sinLat, final double sinLon, final double cosLat, final double cosLon) { + this(computeMagnitude(planetModel, cosLat * cosLon, cosLat * sinLon, sinLat), + cosLat * cosLon, cosLat * sinLon, sinLat); } - public GeoPoint(final double lat, final double lon) { - this(Math.sin(lat), Math.sin(lon), Math.cos(lat), Math.cos(lon)); + public GeoPoint(final PlanetModel planetModel, final double lat, final double lon) { + this(planetModel, Math.sin(lat), Math.sin(lon), Math.cos(lat), Math.cos(lon)); } + public GeoPoint(final double magnitude, final double x, final double y, final double z) { + super(x * magnitude, y * magnitude, z * magnitude); + this.magnitude = magnitude; + } + public GeoPoint(final double x, final double y, final double z) { super(x, y, z); } public double arcDistance(final GeoPoint v) { - return Tools.safeAcos(dotProduct(v)); + return Tools.safeAcos(dotProduct(v)/(magnitude() * v.magnitude())); } + @Override + public double magnitude() { + if (this.magnitude == Double.NEGATIVE_INFINITY) { + this.magnitude = super.magnitude(); + } + return magnitude; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonFactory.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonFactory.java index b630dc0deb6..0f1b48c990b 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonFactory.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonFactory.java @@ -38,16 +38,16 @@ public class GeoPolygonFactory { * its neighbors determines inside/outside for the entire polygon. * @return a GeoMembershipShape corresponding to what was specified. */ - public static GeoMembershipShape makeGeoPolygon(List pointList, int convexPointIndex) { + public static GeoMembershipShape makeGeoPolygon(final PlanetModel planetModel, final List pointList, final int convexPointIndex) { // The basic operation uses a set of points, two points determining one particular edge, and a sided plane // describing membership. - return buildPolygonShape(pointList, convexPointIndex, getLegalIndex(convexPointIndex + 1, pointList.size()), + return buildPolygonShape(planetModel, pointList, convexPointIndex, getLegalIndex(convexPointIndex + 1, pointList.size()), new SidedPlane(pointList.get(getLegalIndex(convexPointIndex - 1, pointList.size())), pointList.get(convexPointIndex), pointList.get(getLegalIndex(convexPointIndex + 1, pointList.size()))), false); } - public static GeoMembershipShape buildPolygonShape(List pointsList, int startPointIndex, int endPointIndex, SidedPlane startingEdge, boolean isInternalEdge) { + public static GeoMembershipShape buildPolygonShape(final PlanetModel planetModel, final List pointsList, final int startPointIndex, final int endPointIndex, final SidedPlane startingEdge, final boolean isInternalEdge) { // Algorithm as follows: // Start with sided edge. Go through all points in some order. For each new point, determine if the point is within all edges considered so far. // If not, put it into a list of points for recursion. If it is within, add new edge and keep going. @@ -112,7 +112,7 @@ public class GeoPolygonFactory { } // We want the other side for the recursion SidedPlane otherSideNewBoundary = new SidedPlane(newBoundary); - rval.addShape(buildPolygonShape(recursionList, recursionList.size() - 2, recursionList.size() - 1, otherSideNewBoundary, true)); + rval.addShape(buildPolygonShape(planetModel, recursionList, recursionList.size() - 2, recursionList.size() - 1, otherSideNewBoundary, true)); recursionList.clear(); } currentList.add(newPoint); @@ -140,11 +140,11 @@ public class GeoPolygonFactory { SidedPlane newBoundary = new SidedPlane(currentList.get(currentList.size() - 2), currentList.get(0), currentList.get(currentList.size() - 1)); // We want the other side for the recursion SidedPlane otherSideNewBoundary = new SidedPlane(newBoundary); - rval.addShape(buildPolygonShape(recursionList, recursionList.size() - 2, recursionList.size() - 1, otherSideNewBoundary, true)); + rval.addShape(buildPolygonShape(planetModel, recursionList, recursionList.size() - 2, recursionList.size() - 1, otherSideNewBoundary, true)); recursionList.clear(); } // Now, add in the current shape. - rval.addShape(new GeoConvexPolygon(currentList, internalEdgeList, returnEdgeInternalBoundary)); + rval.addShape(new GeoConvexPolygon(planetModel, currentList, internalEdgeList, returnEdgeInternalBoundary)); //System.out.println("Done creating polygon"); return rval; } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java index 459203641ac..166a6684446 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java @@ -24,7 +24,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoRectangle extends GeoBBoxBase { +public class GeoRectangle extends GeoBaseBBox { public final double topLat; public final double bottomLat; public final double leftLon; @@ -54,7 +54,8 @@ public class GeoRectangle extends GeoBBoxBase { /** * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} */ - public GeoRectangle(final double topLat, final double bottomLat, final double leftLon, double rightLon) { + public GeoRectangle(final PlanetModel planetModel, final double topLat, final double bottomLat, final double leftLon, double rightLon) { + super(planetModel); // Argument checking if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) throw new IllegalArgumentException("Top latitude out of range"); @@ -88,10 +89,10 @@ public class GeoRectangle extends GeoBBoxBase { final double cosRightLon = Math.cos(rightLon); // Now build the four points - this.ULHC = new GeoPoint(sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); - this.URHC = new GeoPoint(sinTopLat, sinRightLon, cosTopLat, cosRightLon); - this.LRHC = new GeoPoint(sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); - this.LLHC = new GeoPoint(sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); + this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); + this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon); + this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); + this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); final double middleLat = (topLat + bottomLat) * 0.5; final double sinMiddleLat = Math.sin(middleLat); @@ -104,10 +105,10 @@ public class GeoRectangle extends GeoBBoxBase { final double sinMiddleLon = Math.sin(middleLon); final double cosMiddleLon = Math.cos(middleLon); - this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + this.centerPoint = new GeoPoint(planetModel, sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); - this.topPlane = new SidedPlane(centerPoint, sinTopLat); - this.bottomPlane = new SidedPlane(centerPoint, sinBottomLat); + this.topPlane = new SidedPlane(centerPoint, planetModel, sinTopLat); + this.bottomPlane = new SidedPlane(centerPoint, planetModel, sinBottomLat); this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); @@ -133,7 +134,7 @@ public class GeoRectangle extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon); } @Override @@ -180,10 +181,10 @@ public class GeoRectangle extends GeoBBoxBase { @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { - return p.intersects(topPlane, notablePoints, topPlanePoints, bounds, bottomPlane, leftPlane, rightPlane) || - p.intersects(bottomPlane, notablePoints, bottomPlanePoints, bounds, topPlane, leftPlane, rightPlane) || - p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, rightPlane, topPlane, bottomPlane) || - p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane, bottomPlane); + return p.intersects(planetModel, topPlane, notablePoints, topPlanePoints, bounds, bottomPlane, leftPlane, rightPlane) || + p.intersects(planetModel, bottomPlane, notablePoints, bottomPlanePoints, bounds, topPlane, leftPlane, rightPlane) || + p.intersects(planetModel, leftPlane, notablePoints, leftPlanePoints, bounds, rightPlane, topPlane, bottomPlane) || + p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane, bottomPlane); } /** @@ -246,19 +247,20 @@ public class GeoRectangle extends GeoBBoxBase { if (!(o instanceof GeoRectangle)) return false; GeoRectangle other = (GeoRectangle) o; - return other.ULHC.equals(ULHC) && other.LRHC.equals(LRHC); + return super.equals(other) && other.ULHC.equals(ULHC) && other.LRHC.equals(LRHC); } @Override public int hashCode() { - int result = ULHC.hashCode(); - result = 31 * result + LRHC.hashCode(); + int result = super.hashCode(); + result = 31 * result + ULHC.hashCode(); + result = 31 * result + LRHC.hashCode(); return result; } @Override public String toString() { - return "GeoRectangle: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + return "GeoRectangle: {planetmodel="+planetModel+", toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java index 8bff3acca78..aa8ae353fba 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java @@ -22,7 +22,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoSouthLatitudeZone extends GeoBBoxBase { +public class GeoSouthLatitudeZone extends GeoBaseBBox { public final double topLat; public final double cosTopLat; public final SidedPlane topPlane; @@ -34,22 +34,20 @@ public class GeoSouthLatitudeZone extends GeoBBoxBase { // Edge points public final GeoPoint[] edgePoints; - public GeoSouthLatitudeZone(final double topLat) { + public GeoSouthLatitudeZone(final PlanetModel planetModel, final double topLat) { + super(planetModel); this.topLat = topLat; final double sinTopLat = Math.sin(topLat); this.cosTopLat = Math.cos(topLat); - // Construct sample points, so we get our sidedness right - final Vector topPoint = new Vector(0.0, 0.0, sinTopLat); - // Compute an interior point. Pick one whose lat is between top and bottom. final double middleLat = (topLat - Math.PI * 0.5) * 0.5; final double sinMiddleLat = Math.sin(middleLat); - this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 0.0, sinMiddleLat); - this.topBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinTopLat * sinTopLat), 0.0, sinTopLat); + this.interiorPoint = new GeoPoint(planetModel, sinMiddleLat, 0.0, Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 1.0); + this.topBoundaryPoint = new GeoPoint(planetModel, sinTopLat, 0.0, Math.sqrt(1.0 - sinTopLat * sinTopLat), 1.0); - this.topPlane = new SidedPlane(interiorPoint, sinTopLat); + this.topPlane = new SidedPlane(interiorPoint, planetModel, sinTopLat); this.edgePoints = new GeoPoint[]{topBoundaryPoint}; } @@ -58,7 +56,7 @@ public class GeoSouthLatitudeZone extends GeoBBoxBase { public GeoBBox expand(final double angle) { final double newTopLat = topLat + angle; final double newBottomLat = -Math.PI * 0.5; - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, -Math.PI, Math.PI); } @Override @@ -98,7 +96,7 @@ public class GeoSouthLatitudeZone extends GeoBBoxBase { @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { - return p.intersects(topPlane, notablePoints, planePoints, bounds); + return p.intersects(planetModel, topPlane, notablePoints, planePoints, bounds); } /** @@ -155,18 +153,19 @@ public class GeoSouthLatitudeZone extends GeoBBoxBase { if (!(o instanceof GeoSouthLatitudeZone)) return false; GeoSouthLatitudeZone other = (GeoSouthLatitudeZone) o; - return other.topPlane.equals(topPlane); + return super.equals(other) && other.topBoundaryPoint.equals(topBoundaryPoint); } @Override public int hashCode() { - int result = topPlane.hashCode(); + int result = super.hashCode(); + result = 31 * result + topBoundaryPoint.hashCode(); return result; } @Override public String toString() { - return "GeoSouthLatitudeZone: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + ")}"; + return "GeoSouthLatitudeZone: {planetmodel="+planetModel+", toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java index 1b79367dc5b..1270fef4be9 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java @@ -25,7 +25,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoSouthRectangle extends GeoBBoxBase { +public class GeoSouthRectangle extends GeoBaseBBox { public final double topLat; public final double leftLon; public final double rightLon; @@ -45,12 +45,13 @@ public class GeoSouthRectangle extends GeoBBoxBase { public final GeoPoint centerPoint; - public final GeoPoint[] edgePoints = new GeoPoint[]{SOUTH_POLE}; + public final GeoPoint[] edgePoints; /** * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} */ - public GeoSouthRectangle(final double topLat, final double leftLon, double rightLon) { + public GeoSouthRectangle(final PlanetModel planetModel, final double topLat, final double leftLon, double rightLon) { + super(planetModel); // Argument checking if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) throw new IllegalArgumentException("Top latitude out of range"); @@ -77,8 +78,8 @@ public class GeoSouthRectangle extends GeoBBoxBase { final double cosRightLon = Math.cos(rightLon); // Now build the four points - this.ULHC = new GeoPoint(sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); - this.URHC = new GeoPoint(sinTopLat, sinRightLon, cosTopLat, cosRightLon); + this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); + this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon); final double middleLat = (topLat - Math.PI * 0.5) * 0.5; final double sinMiddleLat = Math.sin(middleLat); @@ -91,15 +92,18 @@ public class GeoSouthRectangle extends GeoBBoxBase { final double sinMiddleLon = Math.sin(middleLon); final double cosMiddleLon = Math.cos(middleLon); - this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + this.centerPoint = new GeoPoint(planetModel, sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); - this.topPlane = new SidedPlane(centerPoint, sinTopLat); + this.topPlane = new SidedPlane(centerPoint, planetModel, sinTopLat); this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); this.topPlanePoints = new GeoPoint[]{ULHC, URHC}; - this.leftPlanePoints = new GeoPoint[]{ULHC, SOUTH_POLE}; - this.rightPlanePoints = new GeoPoint[]{URHC, SOUTH_POLE}; + this.leftPlanePoints = new GeoPoint[]{ULHC, planetModel.SOUTH_POLE}; + this.rightPlanePoints = new GeoPoint[]{URHC, planetModel.SOUTH_POLE}; + + this.edgePoints = new GeoPoint[]{planetModel.SOUTH_POLE}; + } @Override @@ -116,7 +120,7 @@ public class GeoSouthRectangle extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon); } @Override @@ -160,9 +164,9 @@ public class GeoSouthRectangle extends GeoBBoxBase { @Override public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { - return p.intersects(topPlane, notablePoints, topPlanePoints, bounds, leftPlane, rightPlane) || - p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, rightPlane, topPlane) || - p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane); + return p.intersects(planetModel, topPlane, notablePoints, topPlanePoints, bounds, leftPlane, rightPlane) || + p.intersects(planetModel, leftPlane, notablePoints, leftPlanePoints, bounds, rightPlane, topPlane) || + p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane); } /** @@ -192,7 +196,7 @@ public class GeoSouthRectangle extends GeoBBoxBase { return OVERLAPS; } - final boolean insideShape = path.isWithin(SOUTH_POLE); + final boolean insideShape = path.isWithin(planetModel.SOUTH_POLE); if (insideRectangle == ALL_INSIDE && insideShape) { //System.err.println(" inside of each other"); @@ -224,19 +228,20 @@ public class GeoSouthRectangle extends GeoBBoxBase { if (!(o instanceof GeoSouthRectangle)) return false; GeoSouthRectangle other = (GeoSouthRectangle) o; - return other.ULHC.equals(ULHC) && other.URHC.equals(URHC); + return super.equals(other) && other.ULHC.equals(ULHC) && other.URHC.equals(URHC); } @Override public int hashCode() { - int result = ULHC.hashCode(); + int result = super.hashCode(); + result = 31 * result + ULHC.hashCode(); result = 31 * result + URHC.hashCode(); return result; } @Override public String toString() { - return "GeoSouthRectangle: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + return "GeoSouthRectangle: {planetmodel="+planetModel+", toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java index a22d6f43990..0346dd04830 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java @@ -22,7 +22,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase { +public class GeoWideDegenerateHorizontalLine extends GeoBaseBBox { public final double latitude; public final double leftLon; public final double rightLon; @@ -46,7 +46,8 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase { * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. * Horizontal angle must be greater than or equal to PI. */ - public GeoWideDegenerateHorizontalLine(final double latitude, final double leftLon, double rightLon) { + public GeoWideDegenerateHorizontalLine(final PlanetModel planetModel, final double latitude, final double leftLon, double rightLon) { + super(planetModel); // Argument checking if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5) throw new IllegalArgumentException("Latitude out of range"); @@ -73,10 +74,10 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase { final double cosRightLon = Math.cos(rightLon); // Now build the two points - this.LHC = new GeoPoint(sinLatitude, sinLeftLon, cosLatitude, cosLeftLon); - this.RHC = new GeoPoint(sinLatitude, sinRightLon, cosLatitude, cosRightLon); + this.LHC = new GeoPoint(planetModel, sinLatitude, sinLeftLon, cosLatitude, cosLeftLon); + this.RHC = new GeoPoint(planetModel, sinLatitude, sinRightLon, cosLatitude, cosRightLon); - this.plane = new Plane(sinLatitude); + this.plane = new Plane(planetModel, sinLatitude); // Normalize while (leftLon > rightLon) { @@ -86,7 +87,7 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase { double sinMiddleLon = Math.sin(middleLon); double cosMiddleLon = Math.cos(middleLon); - this.centerPoint = new GeoPoint(sinLatitude, sinMiddleLon, cosLatitude, cosMiddleLon); + this.centerPoint = new GeoPoint(planetModel, sinLatitude, sinMiddleLon, cosLatitude, cosMiddleLon); this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); @@ -112,7 +113,7 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon); } @Override @@ -160,7 +161,7 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase { public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. - return p.intersects(plane, notablePoints, planePoints, bounds, eitherBound); + return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, eitherBound); } /** @@ -199,19 +200,20 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase { if (!(o instanceof GeoWideDegenerateHorizontalLine)) return false; GeoWideDegenerateHorizontalLine other = (GeoWideDegenerateHorizontalLine) o; - return other.LHC.equals(LHC) && other.RHC.equals(RHC); + return super.equals(other) && other.LHC.equals(LHC) && other.RHC.equals(RHC); } @Override public int hashCode() { - int result = LHC.hashCode(); + int result = super.hashCode(); + result = 31 * result + LHC.hashCode(); result = 31 * result + RHC.hashCode(); return result; } @Override public String toString() { - return "GeoWideDegenerateHorizontalLine: {latitude=" + latitude + "(" + latitude * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightLon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + return "GeoWideDegenerateHorizontalLine: {planetmodel="+planetModel+", latitude=" + latitude + "(" + latitude * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightLon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; } protected class EitherBound implements Membership { diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java index 4ebae4df52d..ff24c496dc1 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java @@ -23,24 +23,25 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoWideLongitudeSlice extends GeoBBoxBase { +public class GeoWideLongitudeSlice extends GeoBaseBBox { public final double leftLon; public final double rightLon; public final SidedPlane leftPlane; public final SidedPlane rightPlane; - public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE, SOUTH_POLE}; + public final GeoPoint[] planePoints; public final GeoPoint centerPoint; - public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; + public final GeoPoint[] edgePoints; /** * Accepts only values in the following ranges: lon: {@code -PI -> PI}. * Horizantal angle must be greater than or equal to PI. */ - public GeoWideLongitudeSlice(final double leftLon, double rightLon) { + public GeoWideLongitudeSlice(final PlanetModel planetModel, final double leftLon, double rightLon) { + super(planetModel); // Argument checking if (leftLon < -Math.PI || leftLon > Math.PI) throw new IllegalArgumentException("Left longitude out of range"); @@ -66,10 +67,13 @@ public class GeoWideLongitudeSlice extends GeoBBoxBase { rightLon += Math.PI * 2.0; } final double middleLon = (leftLon + rightLon) * 0.5; - this.centerPoint = new GeoPoint(0.0, middleLon); + this.centerPoint = new GeoPoint(planetModel, 0.0, middleLon); this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); + + this.planePoints = new GeoPoint[]{planetModel.NORTH_POLE, planetModel.SOUTH_POLE}; + this.edgePoints = new GeoPoint[]{planetModel.NORTH_POLE}; } @Override @@ -84,7 +88,7 @@ public class GeoWideLongitudeSlice extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, Math.PI * 0.5, -Math.PI * 0.5, newLeftLon, newRightLon); } @Override @@ -127,8 +131,8 @@ public class GeoWideLongitudeSlice extends GeoBBoxBase { public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. - return p.intersects(leftPlane, notablePoints, planePoints, bounds) || - p.intersects(rightPlane, notablePoints, planePoints, bounds); + return p.intersects(planetModel, leftPlane, notablePoints, planePoints, bounds) || + p.intersects(planetModel, rightPlane, notablePoints, planePoints, bounds); } /** @@ -155,7 +159,7 @@ public class GeoWideLongitudeSlice extends GeoBBoxBase { if (insideRectangle == SOME_INSIDE) return OVERLAPS; - final boolean insideShape = path.isWithin(NORTH_POLE); + final boolean insideShape = path.isWithin(planetModel.NORTH_POLE); if (insideRectangle == ALL_INSIDE && insideShape) return OVERLAPS; @@ -178,15 +182,14 @@ public class GeoWideLongitudeSlice extends GeoBBoxBase { if (!(o instanceof GeoWideLongitudeSlice)) return false; GeoWideLongitudeSlice other = (GeoWideLongitudeSlice) o; - return other.leftLon == leftLon && other.rightLon == rightLon; + return super.equals(other) && other.leftLon == leftLon && other.rightLon == rightLon; } @Override public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(leftLon); - result = (int) (temp ^ (temp >>> 32)); + int result = super.hashCode(); + long temp = Double.doubleToLongBits(leftLon); + result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(rightLon); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; @@ -194,7 +197,7 @@ public class GeoWideLongitudeSlice extends GeoBBoxBase { @Override public String toString() { - return "GeoWideLongitudeSlice: {leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + return "GeoWideLongitudeSlice: {planetmodel="+planetModel+", leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java index c46abcff378..134dad15c43 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java @@ -23,7 +23,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoWideNorthRectangle extends GeoBBoxBase { +public class GeoWideNorthRectangle extends GeoBaseBBox { public final double bottomLat; public final double leftLon; public final double rightLon; @@ -45,13 +45,14 @@ public class GeoWideNorthRectangle extends GeoBBoxBase { public final EitherBound eitherBound; - public final GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; + public final GeoPoint[] edgePoints; /** * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. * Horizontal angle must be greater than or equal to PI. */ - public GeoWideNorthRectangle(final double bottomLat, final double leftLon, double rightLon) { + public GeoWideNorthRectangle(final PlanetModel planetModel, final double bottomLat, final double leftLon, double rightLon) { + super(planetModel); // Argument checking if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) throw new IllegalArgumentException("Bottom latitude out of range"); @@ -78,8 +79,8 @@ public class GeoWideNorthRectangle extends GeoBBoxBase { final double cosRightLon = Math.cos(rightLon); // Now build the four points - this.LRHC = new GeoPoint(sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); - this.LLHC = new GeoPoint(sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); + this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); + this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5; final double sinMiddleLat = Math.sin(middleLat); @@ -92,17 +93,18 @@ public class GeoWideNorthRectangle extends GeoBBoxBase { final double sinMiddleLon = Math.sin(middleLon); final double cosMiddleLon = Math.cos(middleLon); - this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + this.centerPoint = new GeoPoint(planetModel, sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); - this.bottomPlane = new SidedPlane(centerPoint, sinBottomLat); + this.bottomPlane = new SidedPlane(centerPoint, planetModel, sinBottomLat); this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); this.bottomPlanePoints = new GeoPoint[]{LLHC, LRHC}; - this.leftPlanePoints = new GeoPoint[]{NORTH_POLE, LLHC}; - this.rightPlanePoints = new GeoPoint[]{NORTH_POLE, LRHC}; + this.leftPlanePoints = new GeoPoint[]{planetModel.NORTH_POLE, LLHC}; + this.rightPlanePoints = new GeoPoint[]{planetModel.NORTH_POLE, LRHC}; this.eitherBound = new EitherBound(); + this.edgePoints = new GeoPoint[]{planetModel.NORTH_POLE}; } @Override @@ -119,7 +121,7 @@ public class GeoWideNorthRectangle extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon); } @Override @@ -168,9 +170,9 @@ public class GeoWideNorthRectangle extends GeoBBoxBase { // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. return - p.intersects(bottomPlane, notablePoints, bottomPlanePoints, bounds, eitherBound) || - p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, bottomPlane) || - p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, bottomPlane); + p.intersects(planetModel, bottomPlane, notablePoints, bottomPlanePoints, bounds, eitherBound) || + p.intersects(planetModel, leftPlane, notablePoints, leftPlanePoints, bounds, bottomPlane) || + p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, bottomPlane); } /** @@ -200,7 +202,7 @@ public class GeoWideNorthRectangle extends GeoBBoxBase { return OVERLAPS; } - final boolean insideShape = path.isWithin(NORTH_POLE); + final boolean insideShape = path.isWithin(planetModel.NORTH_POLE); if (insideRectangle == ALL_INSIDE && insideShape) { //System.err.println(" both inside each other"); @@ -234,19 +236,20 @@ public class GeoWideNorthRectangle extends GeoBBoxBase { if (!(o instanceof GeoWideNorthRectangle)) return false; GeoWideNorthRectangle other = (GeoWideNorthRectangle) o; - return other.LLHC.equals(LLHC) && other.LRHC.equals(LRHC); + return super.equals(other) && other.LLHC.equals(LLHC) && other.LRHC.equals(LRHC); } @Override public int hashCode() { - int result = LLHC.hashCode(); + int result = super.hashCode(); + result = 31 * result + LLHC.hashCode(); result = 31 * result + LRHC.hashCode(); return result; } @Override public String toString() { - return "GeoWideNorthRectangle: {bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + return "GeoWideNorthRectangle: {planetmodel="+planetModel+", bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; } protected class EitherBound implements Membership { diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java index 2f46404345b..a80b6d1689e 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java @@ -23,7 +23,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoWideRectangle extends GeoBBoxBase { +public class GeoWideRectangle extends GeoBaseBBox { public final double topLat; public final double bottomLat; public final double leftLon; @@ -56,7 +56,8 @@ public class GeoWideRectangle extends GeoBBoxBase { * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. * Horizontal angle must be greater than or equal to PI. */ - public GeoWideRectangle(final double topLat, final double bottomLat, final double leftLon, double rightLon) { + public GeoWideRectangle(final PlanetModel planetModel, final double topLat, final double bottomLat, final double leftLon, double rightLon) { + super(planetModel); // Argument checking if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) throw new IllegalArgumentException("Top latitude out of range"); @@ -90,10 +91,10 @@ public class GeoWideRectangle extends GeoBBoxBase { final double cosRightLon = Math.cos(rightLon); // Now build the four points - this.ULHC = new GeoPoint(sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); - this.URHC = new GeoPoint(sinTopLat, sinRightLon, cosTopLat, cosRightLon); - this.LRHC = new GeoPoint(sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); - this.LLHC = new GeoPoint(sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); + this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); + this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon); + this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); + this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); final double middleLat = (topLat + bottomLat) * 0.5; final double sinMiddleLat = Math.sin(middleLat); @@ -106,10 +107,10 @@ public class GeoWideRectangle extends GeoBBoxBase { final double sinMiddleLon = Math.sin(middleLon); final double cosMiddleLon = Math.cos(middleLon); - this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + this.centerPoint = new GeoPoint(planetModel, sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); - this.topPlane = new SidedPlane(centerPoint, sinTopLat); - this.bottomPlane = new SidedPlane(centerPoint, sinBottomLat); + this.topPlane = new SidedPlane(centerPoint, planetModel, sinTopLat); + this.bottomPlane = new SidedPlane(centerPoint, planetModel, sinBottomLat); this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); @@ -137,7 +138,7 @@ public class GeoWideRectangle extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon); } @Override @@ -186,10 +187,10 @@ public class GeoWideRectangle extends GeoBBoxBase { public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. - return p.intersects(topPlane, notablePoints, topPlanePoints, bounds, bottomPlane, eitherBound) || - p.intersects(bottomPlane, notablePoints, bottomPlanePoints, bounds, topPlane, eitherBound) || - p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, topPlane, bottomPlane) || - p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, topPlane, bottomPlane); + return p.intersects(planetModel, topPlane, notablePoints, topPlanePoints, bounds, bottomPlane, eitherBound) || + p.intersects(planetModel, bottomPlane, notablePoints, bottomPlanePoints, bounds, topPlane, eitherBound) || + p.intersects(planetModel, leftPlane, notablePoints, leftPlanePoints, bounds, topPlane, bottomPlane) || + p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, topPlane, bottomPlane); } /** @@ -253,19 +254,20 @@ public class GeoWideRectangle extends GeoBBoxBase { if (!(o instanceof GeoWideRectangle)) return false; GeoWideRectangle other = (GeoWideRectangle) o; - return other.ULHC.equals(ULHC) && other.LRHC.equals(LRHC); + return super.equals(other) && other.ULHC.equals(ULHC) && other.LRHC.equals(LRHC); } @Override public int hashCode() { - int result = ULHC.hashCode(); + int result = super.hashCode(); + result = 31 * result + ULHC.hashCode(); result = 31 * result + LRHC.hashCode(); return result; } @Override public String toString() { - return "GeoWideRectangle: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + return "GeoWideRectangle: {planetmodel=" + planetModel + ", toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; } protected class EitherBound implements Membership { diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java index 97568cddf56..816f36b05e4 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java @@ -23,7 +23,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoWideSouthRectangle extends GeoBBoxBase { +public class GeoWideSouthRectangle extends GeoBaseBBox { public final double topLat; public final double leftLon; public final double rightLon; @@ -45,13 +45,14 @@ public class GeoWideSouthRectangle extends GeoBBoxBase { public final EitherBound eitherBound; - public final GeoPoint[] edgePoints = new GeoPoint[]{SOUTH_POLE}; + public final GeoPoint[] edgePoints; /** * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. * Horizontal angle must be greater than or equal to PI. */ - public GeoWideSouthRectangle(final double topLat, final double leftLon, double rightLon) { + public GeoWideSouthRectangle(final PlanetModel planetModel, final double topLat, final double leftLon, double rightLon) { + super(planetModel); // Argument checking if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) throw new IllegalArgumentException("Top latitude out of range"); @@ -78,8 +79,8 @@ public class GeoWideSouthRectangle extends GeoBBoxBase { final double cosRightLon = Math.cos(rightLon); // Now build the four points - this.ULHC = new GeoPoint(sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); - this.URHC = new GeoPoint(sinTopLat, sinRightLon, cosTopLat, cosRightLon); + this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); + this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon); final double middleLat = (topLat - Math.PI * 0.5) * 0.5; final double sinMiddleLat = Math.sin(middleLat); @@ -92,17 +93,19 @@ public class GeoWideSouthRectangle extends GeoBBoxBase { final double sinMiddleLon = Math.sin(middleLon); final double cosMiddleLon = Math.cos(middleLon); - this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + this.centerPoint = new GeoPoint(planetModel, sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); - this.topPlane = new SidedPlane(centerPoint, sinTopLat); + this.topPlane = new SidedPlane(centerPoint, planetModel, sinTopLat); this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); this.topPlanePoints = new GeoPoint[]{ULHC, URHC}; - this.leftPlanePoints = new GeoPoint[]{ULHC, SOUTH_POLE}; - this.rightPlanePoints = new GeoPoint[]{URHC, SOUTH_POLE}; + this.leftPlanePoints = new GeoPoint[]{ULHC, planetModel.SOUTH_POLE}; + this.rightPlanePoints = new GeoPoint[]{URHC, planetModel.SOUTH_POLE}; this.eitherBound = new EitherBound(); + + this.edgePoints = new GeoPoint[]{planetModel.SOUTH_POLE}; } @Override @@ -119,7 +122,7 @@ public class GeoWideSouthRectangle extends GeoBBoxBase { newLeftLon = -Math.PI; newRightLon = Math.PI; } - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon); } @Override @@ -165,9 +168,9 @@ public class GeoWideSouthRectangle extends GeoBBoxBase { public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. - return p.intersects(topPlane, notablePoints, topPlanePoints, bounds, eitherBound) || - p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, topPlane) || - p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, topPlane); + return p.intersects(planetModel, topPlane, notablePoints, topPlanePoints, bounds, eitherBound) || + p.intersects(planetModel, leftPlane, notablePoints, leftPlanePoints, bounds, topPlane) || + p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, topPlane); } /** @@ -197,7 +200,7 @@ public class GeoWideSouthRectangle extends GeoBBoxBase { return OVERLAPS; } - final boolean insideShape = path.isWithin(SOUTH_POLE); + final boolean insideShape = path.isWithin(planetModel.SOUTH_POLE); if (insideRectangle == ALL_INSIDE && insideShape) { //System.err.println(" both inside each other"); @@ -230,19 +233,20 @@ public class GeoWideSouthRectangle extends GeoBBoxBase { if (!(o instanceof GeoWideSouthRectangle)) return false; GeoWideSouthRectangle other = (GeoWideSouthRectangle) o; - return other.ULHC.equals(ULHC) && other.URHC.equals(URHC); + return super.equals(other) && other.ULHC.equals(ULHC) && other.URHC.equals(URHC); } @Override public int hashCode() { - int result = ULHC.hashCode(); + int result = super.hashCode(); + result = 31 * result + ULHC.hashCode(); result = 31 * result + URHC.hashCode(); return result; } @Override public String toString() { - return "GeoWideSouthRectangle: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + return "GeoWideSouthRectangle: {planetmodel="+planetModel+", toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; } protected class EitherBound implements Membership { diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java index dac957adf5d..0a6bdedaed5 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java @@ -22,11 +22,13 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * * @lucene.internal */ -public class GeoWorld extends GeoBBoxBase { - protected final static GeoPoint originPoint = new GeoPoint(1.0, 0.0, 0.0); +public class GeoWorld extends GeoBaseBBox { protected final static GeoPoint[] edgePoints = new GeoPoint[0]; - - public GeoWorld() { + protected final GeoPoint originPoint; + + public GeoWorld(final PlanetModel planetModel) { + super(planetModel); + originPoint = new GeoPoint(planetModel.ab, 1.0, 0.0, 0.0); } @Override @@ -100,16 +102,16 @@ public class GeoWorld extends GeoBBoxBase { public boolean equals(Object o) { if (!(o instanceof GeoWorld)) return false; - return true; + return super.equals(o); } @Override public int hashCode() { - return 0; + return super.hashCode(); } @Override public String toString() { - return "GeoWorld"; + return "GeoWorld: {planetmodel="+planetModel+"}"; } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java index b25e5c9fd55..95f19a9c15f 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java @@ -51,11 +51,12 @@ public class Plane extends Vector { /** * Construct a horizontal plane at a specified Z. * - * @param height is the specified Z coordinate. + * @param planetModel is the planet model. + * @param sinLat is the sin(latitude). */ - public Plane(final double height) { + public Plane(final PlanetModel planetModel, final double sinLat) { super(0.0, 0.0, 1.0); - D = -height; + D = -sinLat * computeMagnitude(planetModel, sinLat); } /** @@ -81,6 +82,15 @@ public class Plane extends Vector { this.D = D; } + /** Construct a normalized, vertical plane through an x-y point. If the x-y point is at (0,0), return null. + */ + public static Plane constructNormalizedVerticalPlane(final double x, final double y) { + if (Math.abs(x) < MINIMUM_RESOLUTION && Math.abs(y) < MINIMUM_RESOLUTION) + return null; + final double denom = 1.0 / Math.sqrt(x*x + y*y); + return new Plane(x * denom, y * denom); + } + /** * Evaluate the plane equation for a given point, as represented * by a vector. @@ -289,15 +299,27 @@ public class Plane extends Vector { return new GeoPoint(result.x, result.y, result.z); } + /** + * Public version of findIntersections. + */ + public GeoPoint[] findIntersections(final PlanetModel planetModel, final Plane q, final Membership... bounds) { + if (isNumericallyIdentical(q)) { + return null; + } + return findIntersections(planetModel, q, bounds, NO_BOUNDS); + } + /** * Find the intersection points between two planes, given a set of bounds. * + * @param planetModel is the planet model to use in finding points. * @param q is the plane to intersect with. * @param bounds is the set of bounds. * @param moreBounds is another set of bounds. * @return the intersection point(s) on the unit sphere, if there are any. */ - protected GeoPoint[] findIntersections(final Plane q, final Membership[] bounds, final Membership[] moreBounds) { + protected GeoPoint[] findIntersections(final PlanetModel planetModel, final Plane q, final Membership[] bounds, final Membership[] moreBounds) { + //System.err.println("Looking for intersection between plane "+this+" and plane "+q+" within bounds"); final Vector lineVector = new Vector(this, q); if (Math.abs(lineVector.x) < MINIMUM_RESOLUTION && Math.abs(lineVector.y) < MINIMUM_RESOLUTION && Math.abs(lineVector.z) < MINIMUM_RESOLUTION) { // Degenerate case: parallel planes @@ -363,16 +385,18 @@ public class Plane extends Vector { z0 = 0.0; } - // Once an intersecting line is determined, the next step is to intersect that line with the unit sphere, which + // Once an intersecting line is determined, the next step is to intersect that line with the ellipsoid, which // will yield zero, one, or two points. - // The equation of the sphere is: 1.0 = x^2 + y^2 + z^2. Plugging in the parameterized line values yields: - // 1.0 = (At+A0)^2 + (Bt+B0)^2 + (Ct+C0)^2 - // A^2 t^2 + 2AA0t + A0^2 + B^2 t^2 + 2BB0t + B0^2 + C^2 t^2 + 2CC0t + C0^2 - 1,0 = 0.0 - // [A^2 + B^2 + C^2] t^2 + [2AA0 + 2BB0 + 2CC0] t + [A0^2 + B0^2 + C0^2 - 1,0] = 0.0 + // The ellipsoid equation: 1,0 = x^2/a^2 + y^2/b^2 + z^2/c^2 + // 1.0 = (At+A0)^2/a^2 + (Bt+B0)^2/b^2 + (Ct+C0)^2/c^2 + // A^2 t^2 / a^2 + 2AA0t / a^2 + A0^2 / a^2 + B^2 t^2 / b^2 + 2BB0t / b^2 + B0^2 / b^2 + C^2 t^2 / c^2 + 2CC0t / c^2 + C0^2 / c^2 - 1,0 = 0.0 + // [A^2 / a^2 + B^2 / b^2 + C^2 / c^2] t^2 + [2AA0 / a^2 + 2BB0 / b^2 + 2CC0 / c^2] t + [A0^2 / a^2 + B0^2 / b^2 + C0^2 / c^2 - 1,0] = 0.0 // Use the quadratic formula to determine t values and candidate point(s) - final double A = lineVector.x * lineVector.x + lineVector.y * lineVector.y + lineVector.z * lineVector.z; - final double B = 2.0 * (lineVector.x * x0 + lineVector.y * y0 + lineVector.z * z0); - final double C = x0 * x0 + y0 * y0 + z0 * z0 - 1.0; + final double A = lineVector.x * lineVector.x * planetModel.inverseAbSquared + + lineVector.y * lineVector.y * planetModel.inverseAbSquared + + lineVector.z * lineVector.z * planetModel.inverseCSquared; + final double B = 2.0 * (lineVector.x * x0 * planetModel.inverseAbSquared + lineVector.y * y0 * planetModel.inverseAbSquared + lineVector.z * z0 * planetModel.inverseCSquared); + final double C = x0 * x0 * planetModel.inverseAbSquared + y0 * y0 * planetModel.inverseAbSquared + z0 * z0 * planetModel.inverseCSquared - 1.0; final double BsquaredMinus = B * B - 4.0 * A * C; if (Math.abs(BsquaredMinus) < MINIMUM_RESOLUTION_SQUARED) { @@ -381,6 +405,8 @@ public class Plane extends Vector { // One solution only final double t = -B * inverse2A; GeoPoint point = new GeoPoint(lineVector.x * t + x0, lineVector.y * t + y0, lineVector.z * t + z0); + //System.err.println(" point: "+point); + //verifyPoint(planetModel, point, q); if (point.isWithin(bounds, moreBounds)) return new GeoPoint[]{point}; return NO_POINTS; @@ -393,6 +419,8 @@ public class Plane extends Vector { final double t2 = (-B - sqrtTerm) * inverse2A; GeoPoint point1 = new GeoPoint(lineVector.x * t1 + x0, lineVector.y * t1 + y0, lineVector.z * t1 + z0); GeoPoint point2 = new GeoPoint(lineVector.x * t2 + x0, lineVector.y * t2 + y0, lineVector.z * t2 + z0); + //verifyPoint(planetModel, point1, q); + //verifyPoint(planetModel, point2, q); //System.err.println(" "+point1+" and "+point2); if (point1.isWithin(bounds, moreBounds)) { if (point2.isWithin(bounds, moreBounds)) @@ -408,18 +436,30 @@ public class Plane extends Vector { } } + /* + protected void verifyPoint(final PlanetModel planetModel, final GeoPoint point, final Plane q) { + if (!evaluateIsZero(point)) + throw new RuntimeException("Intersection point not on original plane; point="+point+", plane="+this); + if (!q.evaluateIsZero(point)) + throw new RuntimeException("Intersection point not on intersected plane; point="+point+", plane="+q); + if (Math.abs(point.x * point.x * planetModel.inverseASquared + point.y * point.y * planetModel.inverseBSquared + point.z * point.z * planetModel.inverseCSquared - 1.0) >= MINIMUM_RESOLUTION) + throw new RuntimeException("Intersection point not on ellipsoid; point="+point); + } + */ + /** * Accumulate bounds information for this plane, intersected with another plane * and with the unit sphere. * Updates both latitude and longitude information, using max/min points found * within the specified bounds. * + * @param planetModel is the planet model to use to determine bounding points * @param q is the plane to intersect with. * @param boundsInfo is the info to update with additional bounding information. * @param bounds are the surfaces delineating what's inside the shape. */ - public void recordBounds(final Plane q, final Bounds boundsInfo, final Membership... bounds) { - final GeoPoint[] intersectionPoints = findIntersections(q, bounds, NO_BOUNDS); + public void recordBounds(final PlanetModel planetModel, final Plane q, final Bounds boundsInfo, final Membership... bounds) { + final GeoPoint[] intersectionPoints = findIntersections(planetModel, q, bounds, NO_BOUNDS); for (GeoPoint intersectionPoint : intersectionPoints) { boundsInfo.addPoint(intersectionPoint); } @@ -430,10 +470,11 @@ public class Plane extends Vector { * Updates both latitude and longitude information, using max/min points found * within the specified bounds. * + * @param planetModel is the planet model to use in determining bounds. * @param boundsInfo is the info to update with additional bounding information. * @param bounds are the surfaces delineating what's inside the shape. */ - public void recordBounds(final Bounds boundsInfo, final Membership... bounds) { + public void recordBounds(final PlanetModel planetModel, final Bounds boundsInfo, final Membership... bounds) { // For clarity, load local variables with good names final double A = this.x; final double B = this.y; @@ -442,236 +483,27 @@ public class Plane extends Vector { // Now compute latitude min/max points if (!boundsInfo.checkNoTopLatitudeBound() || !boundsInfo.checkNoBottomLatitudeBound()) { //System.err.println("Looking at latitude for plane "+this); + // With ellipsoids, we really have only one viable way to do this computation. + // Specifically, we compute an appropriate vertical plane, based on the current plane's x-y orientation, and + // then intersect it with this one and with the ellipsoid. This gives us zero, one, or two points to use + // as bounds. + // There is one special case: horizontal circles. These require TWO vertical planes: one for the x, and one for + // the y, and we use all four resulting points in the bounds computation. if ((Math.abs(A) >= MINIMUM_RESOLUTION || Math.abs(B) >= MINIMUM_RESOLUTION)) { - //System.out.println("A = "+A+" B = "+B+" C = "+C+" D = "+D); - // sin (phi) = z - // cos (theta - phi) = D - // sin (theta) = C (the dot product of (0,0,1) and (A,B,C) ) - // Q: what is z? - // - // cos (theta-phi) = cos(theta)cos(phi) + sin(theta)sin(phi) = D - - if (Math.abs(C) < MINIMUM_RESOLUTION) { - // Special case: circle is vertical. - //System.err.println(" Degenerate case; it's vertical circle"); - // cos(phi) = D, and we want sin(phi) = z - // There are two solutions for phi given cos(phi) = D: a positive solution and a negative solution. - // So, when we compute z = sqrt(1-D^2), it's really z = +/- sqrt(1-D^2) . - - double z; - double x; - double y; - - final double denom = 1.0 / (A * A + B * B); - - z = Math.sqrt(1.0 - D * D); - y = -B * D * denom; - x = -A * D * denom; - addPoint(boundsInfo, bounds, x, y, z); - - z = -z; - addPoint(boundsInfo, bounds, x, y, z); - } else if (Math.abs(D) < MINIMUM_RESOLUTION) { - //System.err.println(" Plane through origin case"); - // The general case is degenerate when the plane goes through the origin. - // Luckily there's a pretty good way to figure out the max and min for that case though. - // We find the two z values by computing the angle of the plane's inclination with the normal. - // E.g., if this.z == 1, then our z value is 0, and if this.z == 0, our z value is 1. - // Also if this.z == -1, then z value is 0 again. - // Another way of putting this is that our z = sqrt(this.x^2 + this.y^2). - // - // The only tricky part is computing x and y. - double z; - double x; - double y; - - final double denom = 1.0 / (A * A + B * B); - - z = Math.sqrt((A * A + B * B) / (A * A + B * B + C * C)); - y = -B * (C * z) * denom; - x = -A * (C * z) * denom; - addPoint(boundsInfo, bounds, x, y, z); - - z = -z; - y = -B * (C * z) * denom; - x = -A * (C * z) * denom; - addPoint(boundsInfo, bounds, x, y, z); - - } else { - //System.err.println(" General latitude case"); - // We might be able to identify a specific new latitude maximum or minimum. - // - // cos (theta-phi) = cos(theta)cos(phi) + sin(theta)sin(phi) = D - // - // This is tricky. If cos(phi) = something, and we want to figure out - // what sin(phi) is, in order to capture all solutions we need to recognize - // that sin(phi) = +/- sqrt(1 - cos(phi)^2). Basically, this means that - // whatever solution we find we have to mirror it across the x-y plane, - // and include both +z and -z solutions. - // - // cos (phi) = +/- sqrt(1-sin(phi)^2) = +/- sqrt(1-z^2) - // cos (theta) = +/- sqrt(1-sin(theta)^2) = +/- sqrt(1-C^2) - // - // D = cos(theta)cos(phi) + sin(theta)sin(phi) - // Substitute: - // D = sqrt(1-C^2) * sqrt(1-z^2) -/+ C * z - // Solve for z... - // D +/- Cz = sqrt(1-C^2)*sqrt(1-z^2) = sqrt(1 - z^2 - C^2 + z^2*C^2) - // Square both sides. - // (D +/- Cz)^2 = 1 - z^2 - C^2 + z^2*C^2 - // D^2 +/- 2DCz + C^2*z^2 = 1 - z^2 - C^2 + z^2*C^2 - // D^2 +/- 2DCz = 1 - C^2 - z^2 - // 0 = z^2 +/- 2DCz + (C^2 +D^2-1) = 0 - // - // z = (+/- 2DC +/- sqrt(4*D^2*C^2 - 4*(C^2+D^2-1))) / (2) - // z = +/- DC +/- sqrt(D^2*C^2 + 1 - C^2 - D^2 ) - // = +/- DC +/- sqrt(D^2*C^2 + 1 - C^2 - D^2) - // - // NOTE WELL: The above is clearly degenerate when D = 0. So we'll have to - // code a different solution for that case! - - // To get x and y, we need to plug z into the equations, as follows: - // - // Ax + By = -Cz - D - // x^2 + y^2 = 1 - z^2 - // - // x = (-Cz -D -By) /A - // y = (-Cz -D -Ax) /B - // - // [(-Cz -D -By) /A]^2 + y^2 = 1 - z^2 - // [-Cz -D -By]^2 + A^2*y^2 = A^2 - A^2*z^2 - // C^2*z^2 + D^2 + B^2*y^2 + 2CDz + 2CBzy + 2DBy + A^2*y^2 - A^2 + A^2*z^2 = 0 - // y^2 [A^2 + B^2] + y [2DB + 2CBz] + [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2] = 0 - // - // - // Use quadratic formula, where: - // a = [A^2 + B^2] - // b = [2BD + 2CBz] - // c = [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2] - // - // y = (-[2BD + 2CBz] +/- sqrt([2BD + 2CBz]^2 - 4 * [A^2 + B^2] * [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2]) ) / (2 * [A^2 + B^2]) - // Take out a 2: - // y = (-[DB +CBz] +/- sqrt([DB + CBz]^2 - [A^2 + B^2] * [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2]) ) / [A^2 + B^2] - // - // The sqrt term simplifies: - // - // B^2*D^2 + C^2*B^2*z^2 + 2C*D*B^2*z - [A^2 + B^2] * [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2] = ? - // B^2*D^2 + C^2*B^2*z^2 + 2C*D*B^2*z - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 - // + B^2 * C^2 * z^2 + B^2 * D^2 + 2 * B^2 * CDz - A^2 * B^2 + B^2 * A^2 * z^2] =? - // C^2*B^2*z^2 + 2C*D*B^2*z - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 - // + B^2 * C^2 * z^2 + 2 * B^2 * CDz - A^2 * B^2 + B^2 * A^2 * z^2] =? - // 2C*D*B^2*z - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 - // + 2 * B^2 * CDz - A^2 * B^2 + B^2 * A^2 * z^2] =? - // - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 - // - A^2 * B^2 + B^2 * A^2 * z^2] =? - // - A^2 * [C^2 * z^2 + D^2 + 2 * CDz - A^2 + A^2*z^2 - // - B^2 + B^2 * z^2] =? - // - A^2 * [z^2[A^2 + B^2 + C^2] - [A^2 + B^2 - D^2] + 2CDz] =? - // - A^2 * [z^2 - [A^2 + B^2 - D^2] + 2CDz] =? - // - // y = (-[DB +CBz] +/- A*sqrt([A^2 + B^2 - D^2] - z^2 - 2CDz) ) / [A^2 + B^2] - // - // correspondingly: - // x = (-[DA +CAz] +/- B*sqrt([A^2 + B^2 - D^2] - z^2 - 2CDz) ) / [A^2 + B^2] - // - // However, for the maximum or minimum we seek, the clause inside the sqrt should be zero. If - // it is NOT zero, then we aren't looking at the right z value. - - double z; - double x; - double y; - - double sqrtValue = D * D * C * C + 1.0 - C * C - D * D; - double denom = 1.0 / (A * A + B * B); - if (Math.abs(sqrtValue) < MINIMUM_RESOLUTION_SQUARED) { - //System.err.println(" One latitude solution"); - double insideValue; - double sqrtTerm; - - z = D * C; - // Since we squared both sides of the equation, we may have introduced spurious solutions, so we have to check. - // But the same check applies to BOTH solutions -- the +z one as well as the -z one. - insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C * z) * denom; - x = -A * (D + C * z) * denom; - if (evaluateIsZero(x, y, z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - // Check the solution on the other side of the x-y plane - z = -z; - insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C * z) * denom; - x = -A * (D + C * z) * denom; - if (evaluateIsZero(x, y, z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - } else if (sqrtValue > 0.0) { - //System.err.println(" Two latitude solutions"); - double sqrtResult = Math.sqrt(sqrtValue); - - double insideValue; - double sqrtTerm; - - z = D * C + sqrtResult; - //System.out.println("z= "+z+" D-C*z = " + (D-C*z) + " Math.sqrt(1.0 - z*z - C*C + z*z*C*C) = "+(Math.sqrt(1.0 - z*z - C*C + z*z*C*C))); - // Since we squared both sides of the equation, we may have introduced spurios solutions, so we have to check. - // But the same check applies to BOTH solutions -- the +z one as well as the -z one. - insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; - //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C * z) * denom; - x = -A * (D + C * z) * denom; - if (evaluateIsZero(x, y, z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - // Check the solution on the other side of the x-y plane - z = -z; - insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; - //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C * z) * denom; - x = -A * (D + C * z) * denom; - if (evaluateIsZero(x, y, z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - z = D * C - sqrtResult; - //System.out.println("z= "+z+" D-C*z = " + (D-C*z) + " Math.sqrt(1.0 - z*z - C*C + z*z*C*C) = "+(Math.sqrt(1.0 - z*z - C*C + z*z*C*C))); - // Since we squared both sides of the equation, we may have introduced spurios solutions, so we have to check. - // But the same check applies to BOTH solutions -- the +z one as well as the -z one. - insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; - //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C * z) * denom; - x = -A * (D + C * z) * denom; - if (evaluateIsZero(x, y, z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - // Check the solution on the other side of the x-y plane - z = -z; - insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; - //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C * z) * denom; - x = -A * (D + C * z) * denom; - if (evaluateIsZero(x, y, z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - } + // NOT a horizontal circle! + //System.err.println(" Not a horizontal circle"); + final Plane verticalPlane = constructNormalizedVerticalPlane(A,B); + final GeoPoint[] points = findIntersections(planetModel, verticalPlane, NO_BOUNDS, NO_BOUNDS); + for (final GeoPoint point : points) { + addPoint(boundsInfo, bounds, point.x, point.y, point.z); } } else { - // Horizontal circle. - // Since the recordBounds() method will be called ONLY for planes that constitute edges of a shape, - // we can be sure that some part of the horizontal circle will be part of the boundary, so we don't need - // to check Membership objects. - boundsInfo.addHorizontalCircle(-D * C); + // Horizontal circle. Since a==b, one vertical plane suffices. + final Plane verticalPlane = new Plane(1.0,0.0); + final GeoPoint[] points = findIntersections(planetModel, verticalPlane, NO_BOUNDS, NO_BOUNDS); + // There will always be two points; we only need one. + final GeoPoint point = points[0]; + boundsInfo.addHorizontalCircle(point.z/Math.sqrt(point.x * point.x + point.y * point.y + point.z * point.z)); } //System.err.println("Done latitude bounds"); } @@ -697,8 +529,8 @@ public class Plane extends Vector { // Geometrically, we have a line segment in x-y space. We need to locate the endpoints // of that line. But luckily, we know some things: specifically, since it is a // degenerate situation in projection, the C value had to have been 0. That - // means that our line's endpoints will coincide with the unit circle. All we - // need to do then is to find the intersection of the unit circle and the line + // means that our line's endpoints will coincide with the projected ellipse. All we + // need to do then is to find the intersection of the projected ellipse and the line // equation: // // A x + B y + D = 0 @@ -706,20 +538,20 @@ public class Plane extends Vector { // Since A != 0: // x = (-By - D)/A // - // The unit circle: - // x^2 + y^2 - 1 = 0 + // The projected ellipse: + // x^2/a^2 + y^2/b^2 - 1 = 0 // Substitute: - // [(-By-D)/A]^2 + y^2 -1 = 0 + // [(-By-D)/A]^2/a^2 + y^2/b^2 -1 = 0 // Multiply through by A^2: - // [-By - D]^2 + A^2*y^2 - A^2 = 0 + // [-By - D]^2/a^2 + A^2*y^2/b^2 - A^2 = 0 // Multiply out: - // B^2*y^2 + 2BDy + D^2 + A^2*y^2 - A^2 = 0 + // B^2*y^2/a^2 + 2BDy/a^2 + D^2/a^2 + A^2*y^2/b^2 - A^2 = 0 // Group: - // y^2 * [B^2 + A^2] + y [2BD] + [D^2-A^2] = 0 + // y^2 * [B^2/a^2 + A^2/b^2] + y [2BD/a^2] + [D^2/a^2-A^2] = 0 - a = B * B + A * A; - b = 2.0 * B * D; - c = D * D - A * A; + a = B * B * planetModel.inverseAbSquared + A * A * planetModel.inverseAbSquared; + b = 2.0 * B * D * planetModel.inverseAbSquared; + c = D * D * planetModel.inverseAbSquared - A * A; double sqrtClause = b * b - 4.0 * a * c; @@ -750,9 +582,9 @@ public class Plane extends Vector { // Use equation suitable for B != 0 // Since I != 0, we rewrite: // y = (-Ax - D)/B - a = B * B + A * A; - b = 2.0 * A * D; - c = D * D - B * B; + a = B * B * planetModel.inverseAbSquared + A * A * planetModel.inverseAbSquared; + b = 2.0 * A * D * planetModel.inverseAbSquared; + c = D * D * planetModel.inverseAbSquared - B * B; double sqrtClause = b * b - 4.0 * a * c; @@ -786,25 +618,25 @@ public class Plane extends Vector { // They are for lat/lon calculation purposes only. x-y is meant to be used for longitude determination, // and z for latitude, and that's all the values are good for. - // (1) Intersect the plane and the unit sphere, and project the results into the x-y plane: + // (1) Intersect the plane and the ellipsoid, and project the results into the x-y plane: // From plane: // z = (-Ax - By - D) / C - // From unit sphere: - // x^2 + y^2 + [(-Ax - By - D) / C]^2 = 1 + // From ellipsoid: + // x^2/a^2 + y^2/b^2 + [(-Ax - By - D) / C]^2/c^2 = 1 // Simplify/expand: - // C^2*x^2 + C^2*y^2 + (-Ax - By - D)^2 = C^2 + // C^2*x^2/a^2 + C^2*y^2/b^2 + (-Ax - By - D)^2/c^2 = C^2 // - // x^2 * C^2 + y^2 * C^2 + x^2 * (A^2 + ABxy + ADx) + (ABxy + y^2 * B^2 + BDy) + (ADx + BDy + D^2) = C^2 + // x^2 * C^2/a^2 + y^2 * C^2/b^2 + x^2 * A^2/c^2 + ABxy/c^2 + ADx/c^2 + ABxy/c^2 + y^2 * B^2/c^2 + BDy/c^2 + ADx/c^2 + BDy/c^2 + D^2/c^2 = C^2 // Group: - // [A^2 + C^2] x^2 + [B^2 + C^2] y^2 + [2AB]xy + [2AD]x + [2BD]y + [D^2-C^2] = 0 + // [A^2/c^2 + C^2/a^2] x^2 + [B^2/c^2 + C^2/b^2] y^2 + [2AB/c^2]xy + [2AD/c^2]x + [2BD/c^2]y + [D^2/c^2-C^2] = 0 // For convenience, introduce post-projection coefficient variables to make life easier. // E x^2 + F y^2 + G xy + H x + I y + J = 0 - double E = A * A + C * C; - double F = B * B + C * C; - double G = 2.0 * A * B; - double H = 2.0 * A * D; - double I = 2.0 * B * D; - double J = D * D - C * C; + double E = A * A * planetModel.inverseCSquared + C * C * planetModel.inverseAbSquared; + double F = B * B * planetModel.inverseCSquared + C * C * planetModel.inverseAbSquared; + double G = 2.0 * A * B * planetModel.inverseCSquared; + double H = 2.0 * A * D * planetModel.inverseCSquared; + double I = 2.0 * B * D * planetModel.inverseCSquared; + double J = D * D * planetModel.inverseCSquared - C * C; //System.err.println("E = " + E + " F = " + F + " G = " + G + " H = "+ H + " I = " + I + " J = " + J); @@ -962,6 +794,7 @@ public class Plane extends Vector { * Determine whether the plane intersects another plane within the * bounds provided. * + * @param planetModel is the planet model to use in determining intersection. * @param q is the other plane. * @param notablePoints are points to look at to disambiguate cases when the two planes are identical. * @param moreNotablePoints are additional points to look at to disambiguate cases when the two planes are identical. @@ -969,7 +802,7 @@ public class Plane extends Vector { * @param moreBounds are more bounds. * @return true if there's an intersection. */ - public boolean intersects(final Plane q, final GeoPoint[] notablePoints, final GeoPoint[] moreNotablePoints, final Membership[] bounds, final Membership... moreBounds) { + public boolean intersects(final PlanetModel planetModel, final Plane q, final GeoPoint[] notablePoints, final GeoPoint[] moreNotablePoints, final Membership[] bounds, final Membership... moreBounds) { //System.err.println("Does plane "+this+" intersect with plane "+q); // If the two planes are identical, then the math will find no points of intersection. // So a special case of this is to check for plane equality. But that is not enough, because @@ -994,7 +827,7 @@ public class Plane extends Vector { //System.err.println(" no notable points inside found; no intersection"); return false; } - return findIntersections(q, bounds, moreBounds).length > 0; + return findIntersections(planetModel, q, bounds, moreBounds).length > 0; } /** @@ -1042,8 +875,8 @@ public class Plane extends Vector { /** * Find a sample point on the intersection between two planes and the unit sphere. */ - public GeoPoint getSampleIntersectionPoint(final Plane q) { - final GeoPoint[] intersections = findIntersections(q, NO_BOUNDS, NO_BOUNDS); + public GeoPoint getSampleIntersectionPoint(final PlanetModel planetModel, final Plane q) { + final GeoPoint[] intersections = findIntersections(planetModel, q, NO_BOUNDS, NO_BOUNDS); if (intersections.length == 0) return null; return intersections[0]; diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/PlanetModel.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/PlanetModel.java new file mode 100644 index 00000000000..4e0bc0d607e --- /dev/null +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/PlanetModel.java @@ -0,0 +1,102 @@ +package org.apache.lucene.spatial.spatial4j.geo3d; + +/* + * 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. + */ + +/** + * Holds mathematical constants associated with the model of a planet. + * @lucene.experimental + */ +public class PlanetModel { + + /** Planet model corresponding to sphere. */ + public static final PlanetModel SPHERE = new PlanetModel(1.0,1.0); + + /** Mean radius */ + public static final double WGS84_MEAN = 6371009.0; + /** Polar radius */ + public static final double WGS84_POLAR = 6356752.3; + /** Equatorial radius */ + public static final double WGS84_EQUATORIAL = 6378137.0; + /** Planet model corresponding to WGS84 */ + public static final PlanetModel WGS84 = new PlanetModel(WGS84_EQUATORIAL/WGS84_MEAN, + WGS84_POLAR/WGS84_MEAN); + + // Surface of the planet: + // x^2/a^2 + y^2/b^2 + z^2/c^2 = 1.0 + // Scaling factors are a,b,c. geo3d can only support models where a==b, so use ab instead. + public final double ab; + public final double c; + public final double inverseAb; + public final double inverseC; + public final double inverseAbSquared; + public final double inverseCSquared; + // We do NOT include radius, because all computations in geo3d are in radians, not meters. + + // Compute north and south pole for planet model, since these are commonly used. + public final GeoPoint NORTH_POLE; + public final GeoPoint SOUTH_POLE; + + public PlanetModel(final double ab, final double c) { + this.ab = ab; + this.c = c; + this.inverseAb = 1.0 / ab; + this.inverseC = 1.0 / c; + this.inverseAbSquared = inverseAb * inverseAb; + this.inverseCSquared = inverseC * inverseC; + this.NORTH_POLE = new GeoPoint(c, 0.0, 0.0, 1.0); + this.SOUTH_POLE = new GeoPoint(c, 0.0, 0.0, -1.0); + } + + /** Find the minimum magnitude of all points on the ellipsoid. + */ + public double getMinimumMagnitude() { + return Math.min(this.ab, this.c); + } + + /** Find the maximum magnitude of all points on the ellipsoid. + */ + public double getMaximumMagnitude() { + return Math.max(this.ab, this.c); + } + + @Override + public boolean equals(final Object o) { + if (!(o instanceof PlanetModel)) + return false; + final PlanetModel other = (PlanetModel)o; + return ab == other.ab && c == other.c; + } + + @Override + public int hashCode() { + return Double.hashCode(ab) + Double.hashCode(c); + } + + @Override + public String toString() { + if (this.equals(SPHERE)) { + return "PlanetModel.SPHERE"; + } else if (this.equals(WGS84)) { + return "PlanetModel.WGS84"; + } else { + return "PlanetModel(ab="+ab+" c="+c+")"; + } + } +} + + diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java index 6c0f49d741f..7af361592cc 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java @@ -53,10 +53,11 @@ public class SidedPlane extends Plane implements Membership { * Construct a sided plane from a point and a Z coordinate. * * @param p point to evaluate. - * @param height is the Z coordinate of the plane. + * @param planetModel is the planet model. + * @param sinLat is the sin of the latitude of the plane. */ - public SidedPlane(Vector p, double height) { - super(height); + public SidedPlane(Vector p, final PlanetModel planetModel, double sinLat) { + super(planetModel, sinLat); sigNum = Math.signum(evaluate(p)); } @@ -84,6 +85,28 @@ public class SidedPlane extends Plane implements Membership { sigNum = Math.signum(evaluate(p)); } + /** Construct a sided plane from two points and a third normal vector. + */ + public static SidedPlane constructNormalizedPerpendicularSidedPlane(final Vector insidePoint, + final Vector normalVector, final Vector point1, final Vector point2) { + final Vector pointsVector = new Vector(point1.x - point2.x, point1.y - point2.y, point1.z - point2.z); + final Vector newNormalVector = new Vector(normalVector, pointsVector).normalize(); + // To construct the plane, we now just need D, which is simply the negative of the evaluation of the circle normal vector at one of the points. + return new SidedPlane(insidePoint, newNormalVector, -newNormalVector.dotProduct(point1)); + } + + /** Construct a sided plane from three points. + */ + public static SidedPlane constructNormalizedThreePointSidedPlane(final Vector insidePoint, + final Vector point1, final Vector point2, final Vector point3) { + final Vector planeNormal = new Vector( + new Vector(point1.x - point2.x, point1.y - point2.y, point1.z - point2.z), + new Vector(point2.x - point3.x, point2.y - point3.y, point2.z - point3.z)).normalize(); + if (planeNormal == null) + return null; + return new SidedPlane(insidePoint, planeNormal, -planeNormal.dotProduct(point2)); + } + /** * Check if a point is within this shape. * diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java index 4ea58120ac9..fe84c05611f 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java @@ -114,14 +114,20 @@ public class Vector { */ public boolean isWithin(final Membership[] bounds, final Membership[] moreBounds) { // Return true if the point described is within all provided bounds + //System.err.println(" checking if "+this+" is within bounds"); for (Membership bound : bounds) { - if (bound != null && !bound.isWithin(this)) + if (bound != null && !bound.isWithin(this)) { + //System.err.println(" NOT within "+bound); return false; + } } for (Membership bound : moreBounds) { - if (bound != null && !bound.isWithin(this)) + if (bound != null && !bound.isWithin(this)) { + //System.err.println(" NOT within "+bound); return false; + } } + //System.err.println(" is within"); return true; } @@ -301,6 +307,20 @@ public class Vector { return Math.sqrt(x * x + y * y + z * z); } + /** Compute the magnitude of a vector projected to a given + * planet model. + */ + protected static double computeMagnitude(final PlanetModel planetModel, final double x, final double y, final double z) { + return 1.0 / Math.sqrt(x*x*planetModel.inverseAbSquared + y*y*planetModel.inverseAbSquared + z*z*planetModel.inverseCSquared); + } + + /** Compute the magnitude of a vector projected to a given + * planet model. + */ + protected static double computeMagnitude(final PlanetModel planetModel, final double z) { + return 1.0 / Math.sqrt((1.0-z*z)*planetModel.inverseAbSquared + z*z*planetModel.inverseCSquared); + } + @Override public boolean equals(Object o) { if (!(o instanceof Vector)) diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java index 93a47325894..2cb6b08ef73 100644 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java @@ -39,6 +39,7 @@ import org.apache.lucene.spatial.spatial4j.geo3d.GeoPath; import org.apache.lucene.spatial.spatial4j.geo3d.GeoPoint; import org.apache.lucene.spatial.spatial4j.geo3d.GeoPolygonFactory; import org.apache.lucene.spatial.spatial4j.geo3d.GeoShape; +import org.apache.lucene.spatial.spatial4j.geo3d.PlanetModel; import org.junit.Test; import static com.spatial4j.core.distance.DistanceUtils.DEGREES_TO_RADIANS; @@ -81,12 +82,12 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase { public void testFailure1() throws IOException { setupStrategy(); final List points = new ArrayList(); - points.add(new GeoPoint(18 * DEGREES_TO_RADIANS, -27 * DEGREES_TO_RADIANS)); - points.add(new GeoPoint(-57 * DEGREES_TO_RADIANS, 146 * DEGREES_TO_RADIANS)); - points.add(new GeoPoint(14 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS)); - points.add(new GeoPoint(-15 * DEGREES_TO_RADIANS, 153 * DEGREES_TO_RADIANS)); + points.add(new GeoPoint(PlanetModel.SPHERE, 18 * DEGREES_TO_RADIANS, -27 * DEGREES_TO_RADIANS)); + points.add(new GeoPoint(PlanetModel.SPHERE, -57 * DEGREES_TO_RADIANS, 146 * DEGREES_TO_RADIANS)); + points.add(new GeoPoint(PlanetModel.SPHERE, 14 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS)); + points.add(new GeoPoint(PlanetModel.SPHERE, -15 * DEGREES_TO_RADIANS, 153 * DEGREES_TO_RADIANS)); - final Shape triangle = new Geo3dShape(GeoPolygonFactory.makeGeoPolygon(points,0),ctx); + final Shape triangle = new Geo3dShape(GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points,0),ctx); final Rectangle rect = ctx.makeRectangle(-49, -45, 73, 86); testOperation(rect,SpatialOperation.Intersects,triangle, false); } @@ -101,11 +102,11 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase { private Shape makeTriangle(double x1, double y1, double x2, double y2, double x3, double y3) { final List geoPoints = new ArrayList<>(); - geoPoints.add(new GeoPoint(y1 * DEGREES_TO_RADIANS, x1 * DEGREES_TO_RADIANS)); - geoPoints.add(new GeoPoint(y2 * DEGREES_TO_RADIANS, x2 * DEGREES_TO_RADIANS)); - geoPoints.add(new GeoPoint(y3 * DEGREES_TO_RADIANS, x3 * DEGREES_TO_RADIANS)); + geoPoints.add(new GeoPoint(PlanetModel.SPHERE, y1 * DEGREES_TO_RADIANS, x1 * DEGREES_TO_RADIANS)); + geoPoints.add(new GeoPoint(PlanetModel.SPHERE, y2 * DEGREES_TO_RADIANS, x2 * DEGREES_TO_RADIANS)); + geoPoints.add(new GeoPoint(PlanetModel.SPHERE, y3 * DEGREES_TO_RADIANS, x3 * DEGREES_TO_RADIANS)); final int convexPointIndex = 0; - final GeoShape shape = GeoPolygonFactory.makeGeoPolygon(geoPoints, convexPointIndex); + final GeoShape shape = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, geoPoints, convexPointIndex); return new Geo3dShape(shape, ctx); } @@ -125,12 +126,12 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase { final List geoPoints = new ArrayList<>(); while (geoPoints.size() < vertexCount) { final Point point = randomPoint(); - final GeoPoint gPt = new GeoPoint(point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS); + final GeoPoint gPt = new GeoPoint(PlanetModel.SPHERE, point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS); geoPoints.add(gPt); } final int convexPointIndex = random().nextInt(vertexCount); //If we get this wrong, hopefully we get IllegalArgumentException try { - final GeoShape shape = GeoPolygonFactory.makeGeoPolygon(geoPoints, convexPointIndex); + final GeoShape shape = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, geoPoints, convexPointIndex); return new Geo3dShape(shape, ctx); } catch (IllegalArgumentException e) { // This is what happens when we create a shape that is invalid. Although it is conceivable that there are cases where @@ -145,7 +146,7 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase { final int circleRadius = random().nextInt(179) + 1; final Point point = randomPoint(); try { - final GeoShape shape = new GeoCircle(point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS, + final GeoShape shape = new GeoCircle(PlanetModel.SPHERE, point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS, circleRadius * DEGREES_TO_RADIANS); return new Geo3dShape(shape, ctx); } catch (IllegalArgumentException e) { @@ -167,7 +168,7 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase { lrhcPoint = temp; } try { - final GeoShape shape = GeoBBoxFactory.makeGeoBBox(ulhcPoint.getY() * DEGREES_TO_RADIANS, + final GeoShape shape = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, ulhcPoint.getY() * DEGREES_TO_RADIANS, lrhcPoint.getY() * DEGREES_TO_RADIANS, ulhcPoint.getX() * DEGREES_TO_RADIANS, lrhcPoint.getX() * DEGREES_TO_RADIANS); @@ -186,7 +187,7 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase { final double width = (random().nextInt(89)+1) * DEGREES_TO_RADIANS; while (true) { try { - final GeoPath path = new GeoPath(width); + final GeoPath path = new GeoPath(PlanetModel.SPHERE, width); for (int i = 0; i < pointCount; i++) { final Point nextPoint = randomPoint(); path.addPoint(nextPoint.getY() * DEGREES_TO_RADIANS, nextPoint.getX() * DEGREES_TO_RADIANS); diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java similarity index 74% rename from lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTest.java rename to lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java index b67383b4247..2755d848667 100644 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java @@ -25,9 +25,7 @@ import com.carrotsearch.randomizedtesting.RandomizedContext; import com.spatial4j.core.context.SpatialContext; import com.spatial4j.core.distance.DistanceUtils; import com.spatial4j.core.shape.Point; -import com.spatial4j.core.shape.Rectangle; import org.apache.lucene.spatial.spatial4j.geo3d.Bounds; -import org.apache.lucene.spatial.spatial4j.geo3d.GeoArea; import org.apache.lucene.spatial.spatial4j.geo3d.GeoBBox; import org.apache.lucene.spatial.spatial4j.geo3d.GeoBBoxFactory; import org.apache.lucene.spatial.spatial4j.geo3d.GeoCircle; @@ -35,26 +33,30 @@ import org.apache.lucene.spatial.spatial4j.geo3d.GeoPath; import org.apache.lucene.spatial.spatial4j.geo3d.GeoPoint; import org.apache.lucene.spatial.spatial4j.geo3d.GeoPolygonFactory; import org.apache.lucene.spatial.spatial4j.geo3d.GeoShape; +import org.apache.lucene.spatial.spatial4j.geo3d.PlanetModel; import org.junit.Rule; import org.junit.Test; import static com.spatial4j.core.distance.DistanceUtils.DEGREES_TO_RADIANS; -public class Geo3dShapeRectRelationTest extends RandomizedShapeTestCase { +public abstract class Geo3dShapeRectRelationTestCase extends RandomizedShapeTestCase { + protected final static double RADIANS_PER_DEGREE = Math.PI/180.0; + @Rule public final LogRule testLog = LogRule.instance; - static Random random() { + protected static Random random() { return RandomizedContext.current().getRandom(); } - { - ctx = SpatialContext.GEO; + protected final PlanetModel planetModel; + + public Geo3dShapeRectRelationTestCase(PlanetModel planetModel) { + super(SpatialContext.GEO); + this.planetModel = planetModel; } - protected final static double RADIANS_PER_DEGREE = Math.PI/180.0; - - protected static GeoBBox getBoundingBox(final GeoShape path) { + protected GeoBBox getBoundingBox(final GeoShape path) { Bounds bounds = path.getBounds(null); double leftLon; @@ -78,7 +80,7 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTestCase { } else { maxLat = bounds.getMaxLatitude().doubleValue(); } - return GeoBBoxFactory.makeGeoBBox(maxLat, minLat, leftLon, rightLon); + return GeoBBoxFactory.makeGeoBBox(planetModel, maxLat, minLat, leftLon, rightLon); } @Test @@ -91,9 +93,9 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTestCase { final int circleRadius = random().nextInt(179) + 1;//no 0-radius final Point point = nearP; try { - final GeoShape shape = new GeoCircle(point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS, + final GeoShape shape = new GeoCircle(planetModel, point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS, circleRadius * DEGREES_TO_RADIANS); - return new Geo3dShape(shape, ctx); + return new Geo3dShape(planetModel, shape, ctx); } catch (IllegalArgumentException e) { // This is what happens when we create a shape that is invalid. Although it is conceivable that there are cases where // the exception is thrown incorrectly, we aren't going to be able to do that in this random test. @@ -131,11 +133,11 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTestCase { ulhcPoint = lrhcPoint; lrhcPoint = temp; } - final GeoShape shape = GeoBBoxFactory.makeGeoBBox(ulhcPoint.getY() * DEGREES_TO_RADIANS, + final GeoShape shape = GeoBBoxFactory.makeGeoBBox(planetModel, ulhcPoint.getY() * DEGREES_TO_RADIANS, lrhcPoint.getY() * DEGREES_TO_RADIANS, ulhcPoint.getX() * DEGREES_TO_RADIANS, lrhcPoint.getX() * DEGREES_TO_RADIANS); - return new Geo3dShape(shape, ctx); + return new Geo3dShape(planetModel, shape, ctx); } @Override @@ -160,13 +162,13 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTestCase { final Point point = randomPoint(); if (ctx.getDistCalc().distance(point,centerPoint) > maxDistance) continue; - final GeoPoint gPt = new GeoPoint(point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS); + final GeoPoint gPt = new GeoPoint(planetModel, point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS); geoPoints.add(gPt); } final int convexPointIndex = random().nextInt(vertexCount); //If we get this wrong, hopefully we get IllegalArgumentException try { - final GeoShape shape = GeoPolygonFactory.makeGeoPolygon(geoPoints, convexPointIndex); - return new Geo3dShape(shape, ctx); + final GeoShape shape = GeoPolygonFactory.makeGeoPolygon(planetModel, geoPoints, convexPointIndex); + return new Geo3dShape(planetModel, shape, ctx); } catch (IllegalArgumentException e) { // This is what happens when we create a shape that is invalid. Although it is conceivable that there are cases where // the exception is thrown incorrectly, we aren't going to be able to do that in this random test. @@ -201,7 +203,7 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTestCase { final double width = (random().nextInt(89)+1) * DEGREES_TO_RADIANS; while (true) { try { - final GeoPath path = new GeoPath(width); + final GeoPath path = new GeoPath(planetModel, width); int i = 0; while (i < pointCount) { final Point nextPoint = randomPoint(); @@ -211,7 +213,7 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTestCase { i++; } path.done(); - return new Geo3dShape(path, ctx); + return new Geo3dShape(planetModel, path, ctx); } catch (IllegalArgumentException e) { // This is what happens when we create a shape that is invalid. Although it is conceivable that there are cases where // the exception is thrown incorrectly, we aren't going to be able to do that in this random test. @@ -238,38 +240,4 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTestCase { return ctx.makePoint(geoPoint.x * DistanceUtils.RADIANS_TO_DEGREES, geoPoint.y * DistanceUtils.RADIANS_TO_DEGREES); } - - @Test - public void testFailure1() { - final GeoBBox rect = GeoBBoxFactory.makeGeoBBox(88 * RADIANS_PER_DEGREE, 30 * RADIANS_PER_DEGREE, -30 * RADIANS_PER_DEGREE, 62 * RADIANS_PER_DEGREE); - final List points = new ArrayList(); - points.add(new GeoPoint(66.2465299717 * RADIANS_PER_DEGREE, -29.1786158537 * RADIANS_PER_DEGREE)); - points.add(new GeoPoint(43.684447915 * RADIANS_PER_DEGREE, 46.2210986329 * RADIANS_PER_DEGREE)); - points.add(new GeoPoint(30.4579218227 * RADIANS_PER_DEGREE, 14.5238410082 * RADIANS_PER_DEGREE)); - final GeoShape path = GeoPolygonFactory.makeGeoPolygon(points,0); - - final GeoPoint point = new GeoPoint(34.2730264413182 * RADIANS_PER_DEGREE, 82.75500168892472 * RADIANS_PER_DEGREE); - - // Apparently the rectangle thinks the polygon is completely within it... "shape inside rectangle" - assertTrue(GeoArea.WITHIN == rect.getRelationship(path)); - - // Point is within path? Apparently not... - assertFalse(path.isWithin(point)); - - // If it is within the path, it must be within the rectangle, and similarly visa versa - assertFalse(rect.isWithin(point)); - - } - - @Test - public void testFailure2_LUCENE6475() { - GeoShape geo3dCircle = new GeoCircle(1.6282053147165243E-4 * RADIANS_PER_DEGREE, - -70.1600629789353 * RADIANS_PER_DEGREE, 86 * RADIANS_PER_DEGREE); - Geo3dShape geo3dShape = new Geo3dShape(geo3dCircle, ctx); - Rectangle rect = ctx.makeRectangle(-118, -114, -2.0, 32.0); - assertTrue(geo3dShape.relate(rect).intersects()); - // thus the bounding box must intersect too - assertTrue(geo3dShape.getBoundingBox().relate(rect).intersects()); - - } } diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java new file mode 100644 index 00000000000..5e2ca7d6664 --- /dev/null +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java @@ -0,0 +1,73 @@ +package org.apache.lucene.spatial.spatial4j; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.List; + +import com.spatial4j.core.shape.Rectangle; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoArea; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoBBox; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoBBoxFactory; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoCircle; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoPoint; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoPolygonFactory; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoShape; +import org.apache.lucene.spatial.spatial4j.geo3d.PlanetModel; +import org.junit.Test; + +public class Geo3dShapeSphereModelRectRelationTest extends Geo3dShapeRectRelationTestCase { + + public Geo3dShapeSphereModelRectRelationTest() { + super(PlanetModel.SPHERE); + } + + @Test + public void testFailure1() { + final GeoBBox rect = GeoBBoxFactory.makeGeoBBox(planetModel, 88 * RADIANS_PER_DEGREE, 30 * RADIANS_PER_DEGREE, -30 * RADIANS_PER_DEGREE, 62 * RADIANS_PER_DEGREE); + final List points = new ArrayList<>(); + points.add(new GeoPoint(planetModel, 66.2465299717 * RADIANS_PER_DEGREE, -29.1786158537 * RADIANS_PER_DEGREE)); + points.add(new GeoPoint(planetModel, 43.684447915 * RADIANS_PER_DEGREE, 46.2210986329 * RADIANS_PER_DEGREE)); + points.add(new GeoPoint(planetModel, 30.4579218227 * RADIANS_PER_DEGREE, 14.5238410082 * RADIANS_PER_DEGREE)); + final GeoShape path = GeoPolygonFactory.makeGeoPolygon(planetModel, points,0); + + final GeoPoint point = new GeoPoint(planetModel, 34.2730264413182 * RADIANS_PER_DEGREE, 82.75500168892472 * RADIANS_PER_DEGREE); + + // Apparently the rectangle thinks the polygon is completely within it... "shape inside rectangle" + assertTrue(GeoArea.WITHIN == rect.getRelationship(path)); + + // Point is within path? Apparently not... + assertFalse(path.isWithin(point)); + + // If it is within the path, it must be within the rectangle, and similarly visa versa + assertFalse(rect.isWithin(point)); + + } + + @Test + public void testFailure2_LUCENE6475() { + GeoShape geo3dCircle = new GeoCircle(planetModel, 1.6282053147165243E-4 * RADIANS_PER_DEGREE, + -70.1600629789353 * RADIANS_PER_DEGREE, 86 * RADIANS_PER_DEGREE); + Geo3dShape geo3dShape = new Geo3dShape(planetModel, geo3dCircle, ctx); + Rectangle rect = ctx.makeRectangle(-118, -114, -2.0, 32.0); + assertTrue(geo3dShape.relate(rect).intersects()); + // thus the bounding box must intersect too + assertTrue(geo3dShape.getBoundingBox().relate(rect).intersects()); + + } +} diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java new file mode 100644 index 00000000000..b26f1623de6 --- /dev/null +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java @@ -0,0 +1,95 @@ +package org.apache.lucene.spatial.spatial4j; + +/* + * 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. + */ + +import org.apache.lucene.spatial.spatial4j.geo3d.GeoArea; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoBBox; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoBBoxFactory; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoCircle; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoPath; +import org.apache.lucene.spatial.spatial4j.geo3d.GeoPoint; +import org.apache.lucene.spatial.spatial4j.geo3d.PlanetModel; +import org.junit.Test; + +public class Geo3dShapeWGS84ModelRectRelationTest extends Geo3dShapeRectRelationTestCase { + + public Geo3dShapeWGS84ModelRectRelationTest() { + super(PlanetModel.WGS84); + } + + @Test + public void testFailure1() { + final GeoBBox rect = GeoBBoxFactory.makeGeoBBox(planetModel, 90 * RADIANS_PER_DEGREE, 74 * RADIANS_PER_DEGREE, + 40 * RADIANS_PER_DEGREE, 60 * RADIANS_PER_DEGREE); + final GeoPath path = new GeoPath(planetModel, 4 * RADIANS_PER_DEGREE); + path.addPoint(84.4987594274 * RADIANS_PER_DEGREE, -22.8345484402 * RADIANS_PER_DEGREE); + path.done(); + assertTrue(GeoArea.DISJOINT == rect.getRelationship(path)); + // This is what the test failure claimed... + //assertTrue(GeoArea.CONTAINS == rect.getRelationship(path)); + //final GeoBBox bbox = getBoundingBox(path); + //assertFalse(GeoArea.DISJOINT == rect.getRelationship(bbox)); + } + + @Test + public void testFailure2() { + final GeoBBox rect = GeoBBoxFactory.makeGeoBBox(planetModel, -74 * RADIANS_PER_DEGREE, -90 * RADIANS_PER_DEGREE, + 0 * RADIANS_PER_DEGREE, 26 * RADIANS_PER_DEGREE); + final GeoCircle circle = new GeoCircle(planetModel, -87.3647352103 * RADIANS_PER_DEGREE, 52.3769709972 * RADIANS_PER_DEGREE, 1 * RADIANS_PER_DEGREE); + assertTrue(GeoArea.DISJOINT == rect.getRelationship(circle)); + // This is what the test failure claimed... + //assertTrue(GeoArea.CONTAINS == rect.getRelationship(circle)); + //final GeoBBox bbox = getBoundingBox(circle); + //assertFalse(GeoArea.DISJOINT == rect.getRelationship(bbox)); + } + + @Test + public void testFailure3() { + /* + [junit4] 1> S-R Rel: {}, Shape {}, Rectangle {} lap# {} [CONTAINS, Geo3dShape{planetmodel=PlanetModel: {ab=1.0011188180710464, c=0.9977622539852008}, shape=GeoPath: {planetmodel=PlanetModel: {ab=1.0011188180710464, c=0.9977622539852008}, width=1.53588974175501(87.99999999999999), + points={[[X=0.12097657665150223, Y=-0.6754177666095532, Z=0.7265376136709238], [X=-0.3837892785614207, Y=0.4258049113530899, Z=0.8180007850434892]]}}}, + Rect(minX=4.0,maxX=36.0,minY=16.0,maxY=16.0), 6981](no slf4j subst; sorry) + [junit4] FAILURE 0.59s | Geo3dWGS84ShapeRectRelationTest.testGeoPathRect <<< + [junit4] > Throwable #1: java.lang.AssertionError: Geo3dShape{planetmodel=PlanetModel: {ab=1.0011188180710464, c=0.9977622539852008}, shape=GeoPath: {planetmodel=PlanetModel: {ab=1.0011188180710464, c=0.9977622539852008}, width=1.53588974175501(87.99999999999999), + points={[[X=0.12097657665150223, Y=-0.6754177666095532, Z=0.7265376136709238], [X=-0.3837892785614207, Y=0.4258049113530899, Z=0.8180007850434892]]}}} intersect Pt(x=23.81626064835212,y=16.0) + [junit4] > at __randomizedtesting.SeedInfo.seed([2595268DA3F13FEA:6CC30D8C83453E5D]:0) + [junit4] > at org.apache.lucene.spatial.spatial4j.RandomizedShapeTestCase._assertIntersect(RandomizedShapeTestCase.java:168) + [junit4] > at org.apache.lucene.spatial.spatial4j.RandomizedShapeTestCase.assertRelation(RandomizedShapeTestCase.java:153) + [junit4] > at org.apache.lucene.spatial.spatial4j.RectIntersectionTestHelper.testRelateWithRectangle(RectIntersectionTestHelper.java:128) + [junit4] > at org.apache.lucene.spatial.spatial4j.Geo3dWGS84ShapeRectRelationTest.testGeoPathRect(Geo3dWGS84ShapeRectRelationTest.java:265) + */ + final GeoBBox rect = GeoBBoxFactory.makeGeoBBox(planetModel, 16 * RADIANS_PER_DEGREE, 16 * RADIANS_PER_DEGREE, 4 * RADIANS_PER_DEGREE, 36 * RADIANS_PER_DEGREE); + final GeoPoint pt = new GeoPoint(planetModel, 16 * RADIANS_PER_DEGREE, 23.81626064835212 * RADIANS_PER_DEGREE); + final GeoPath path = new GeoPath(planetModel, 88 * RADIANS_PER_DEGREE); + path.addPoint(46.6369060853 * RADIANS_PER_DEGREE, -79.8452213228 * RADIANS_PER_DEGREE); + path.addPoint(54.9779334519 * RADIANS_PER_DEGREE, 132.029177424 * RADIANS_PER_DEGREE); + path.done(); + System.out.println("rect=" + rect); + // Rectangle is within path (this is wrong; it's on the other side. Should be OVERLAPS) + assertTrue(GeoArea.OVERLAPS == rect.getRelationship(path)); + // Rectangle contains point + //assertTrue(rect.isWithin(pt)); + // Path contains point (THIS FAILS) + //assertTrue(path.isWithin(pt)); + // What happens: (1) The center point of the horizontal line is within the path, in fact within a radius of one of the endpoints. + // (2) The point mentioned is NOT inside either SegmentEndpoint. + // (3) The point mentioned is NOT inside the path segment, either. (I think it should be...) + } + +} + diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java index a0c67dff92c..f96fcb73b0d 100755 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java @@ -36,14 +36,14 @@ public class GeoBBoxTest { GeoConvexPolygon cp; int relationship; List points = new ArrayList(); - points.add(new GeoPoint(24 * DEGREES_TO_RADIANS, -30 * DEGREES_TO_RADIANS)); - points.add(new GeoPoint(-11 * DEGREES_TO_RADIANS, 101 * DEGREES_TO_RADIANS)); - points.add(new GeoPoint(-49 * DEGREES_TO_RADIANS, -176 * DEGREES_TO_RADIANS)); - GeoMembershipShape shape = GeoPolygonFactory.makeGeoPolygon(points, 0); - box = GeoBBoxFactory.makeGeoBBox(-64 * DEGREES_TO_RADIANS, -64 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS, 180 * DEGREES_TO_RADIANS); + points.add(new GeoPoint(PlanetModel.SPHERE, 24 * DEGREES_TO_RADIANS, -30 * DEGREES_TO_RADIANS)); + points.add(new GeoPoint(PlanetModel.SPHERE, -11 * DEGREES_TO_RADIANS, 101 * DEGREES_TO_RADIANS)); + points.add(new GeoPoint(PlanetModel.SPHERE, -49 * DEGREES_TO_RADIANS, -176 * DEGREES_TO_RADIANS)); + GeoMembershipShape shape = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points, 0); + box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, -64 * DEGREES_TO_RADIANS, -64 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS, 180 * DEGREES_TO_RADIANS); relationship = box.getRelationship(shape); assertEquals(GeoArea.CONTAINS, relationship); - box = GeoBBoxFactory.makeGeoBBox(-61.85 * DEGREES_TO_RADIANS, -67.5 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS, -168.75 * DEGREES_TO_RADIANS); + box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, -61.85 * DEGREES_TO_RADIANS, -67.5 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS, -168.75 * DEGREES_TO_RADIANS); System.out.println("Shape = " + shape + " Rect = " + box); relationship = box.getRelationship(shape); assertEquals(GeoArea.CONTAINS, relationship); @@ -54,52 +54,52 @@ public class GeoBBoxTest { GeoBBox box; GeoPoint gp; // Standard normal Rect box, not crossing dateline - box = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -1.0, 1.0); - gp = new GeoPoint(-0.1, 0.0); + box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -1.0, 1.0); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 0.0); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(0.1, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, 0.1, 0.0); assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.5, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0); assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1, 1.1); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 1.1); assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1, -1.1); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.1); assertFalse(box.isWithin(gp)); // Standard normal Rect box, crossing dateline - box = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, Math.PI - 1.0, -Math.PI + 1.0); - gp = new GeoPoint(-0.1, -Math.PI); + box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, Math.PI - 1.0, -Math.PI + 1.0); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(0.1, -Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI); assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.5, -Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, -Math.PI); assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1, -Math.PI + 1.1); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1); assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1, -Math.PI - 1.1); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI - 1.1); assertFalse(box.isWithin(gp)); // Latitude zone rectangle - box = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -Math.PI, Math.PI); - gp = new GeoPoint(-0.1, -Math.PI); + box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -Math.PI, Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(0.1, -Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI); assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.5, -Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, -Math.PI); assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1, -Math.PI + 1.1); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-0.1, -Math.PI - 1.1); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI - 1.1); assertTrue(box.isWithin(gp)); // World - box = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, Math.PI); - gp = new GeoPoint(-0.1, -Math.PI); + box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(0.1, -Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.5, -Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, -Math.PI); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-0.1, -Math.PI + 1.1); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-0.1, -Math.PI - 1.1); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI - 1.1); assertTrue(box.isWithin(gp)); } @@ -109,23 +109,23 @@ public class GeoBBoxTest { GeoBBox box; GeoPoint gp; // Standard normal Rect box, not crossing dateline - box = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -1.0, 1.0); + box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -1.0, 1.0); box = box.expand(0.1); - gp = new GeoPoint(0.05, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, 0.05, 0.0); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(0.15, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, 0.15, 0.0); assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.25 - 0.05, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.25 - 0.05, 0.0); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.25 - 0.15, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.25 - 0.15, 0.0); assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1, -1.05); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.05); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-0.1, -1.15); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.15); assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1, 1.05); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 1.05); assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-0.1, 1.15); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 1.15); assertFalse(box.isWithin(gp)); } @@ -134,7 +134,7 @@ public class GeoBBoxTest { GeoBBox c; Bounds b; - c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -1.0, 1.0); + c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -1.0, 1.0); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); @@ -145,7 +145,7 @@ public class GeoBBoxTest { assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001); assertEquals(0.0, b.getMaxLatitude(), 0.000001); - c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, 1.0, -1.0); + c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, 1.0, -1.0); b = c.getBounds(null); assertTrue(b.checkNoLongitudeBound()); @@ -156,7 +156,7 @@ public class GeoBBoxTest { assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001); assertEquals(0.0, b.getMaxLatitude(), 0.000001); - c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -1.0, 1.0); + c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -1.0, 1.0); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); @@ -165,7 +165,7 @@ public class GeoBBoxTest { assertEquals(-1.0, b.getLeftLongitude(), 0.000001); assertEquals(1.0, b.getRightLongitude(), 0.000001); - c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 1.0, -1.0); + c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 1.0, -1.0); b = c.getBounds(null); assertTrue(b.checkNoLongitudeBound()); @@ -176,7 +176,7 @@ public class GeoBBoxTest { // Check wide variants of rectangle and longitude slice - c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -Math.PI + 0.1, Math.PI - 0.1); + c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -Math.PI + 0.1, Math.PI - 0.1); b = c.getBounds(null); assertTrue(b.checkNoLongitudeBound()); @@ -187,7 +187,7 @@ public class GeoBBoxTest { assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001); assertEquals(0.0, b.getMaxLatitude(), 0.000001); - c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, Math.PI - 0.1, -Math.PI + 0.1); + c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, Math.PI - 0.1, -Math.PI + 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); @@ -198,7 +198,7 @@ public class GeoBBoxTest { assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001); assertEquals(0.0, b.getMaxLatitude(), 0.000001); - c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI + 0.1, Math.PI - 0.1); + c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI + 0.1, Math.PI - 0.1); b = c.getBounds(null); assertTrue(b.checkNoLongitudeBound()); @@ -207,7 +207,7 @@ public class GeoBBoxTest { //assertEquals(-Math.PI+0.1,b.getLeftLongitude(),0.000001); //assertEquals(Math.PI-0.1,b.getRightLongitude(),0.000001); - c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, Math.PI - 0.1, -Math.PI + 0.1); + c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, Math.PI - 0.1, -Math.PI + 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); @@ -217,7 +217,7 @@ public class GeoBBoxTest { assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.000001); // Check latitude zone - c = GeoBBoxFactory.makeGeoBBox(1.0, -1.0, -Math.PI, Math.PI); + c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 1.0, -1.0, -Math.PI, Math.PI); b = c.getBounds(null); assertTrue(b.checkNoLongitudeBound()); @@ -230,8 +230,8 @@ public class GeoBBoxTest { GeoBBox c1; GeoBBox c2; - c1 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0); - c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI); + c1 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0); + c2 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI); b = new Bounds(); b = c1.getBounds(b); @@ -240,8 +240,8 @@ public class GeoBBoxTest { assertTrue(b.checkNoTopLatitudeBound()); assertTrue(b.checkNoBottomLatitudeBound()); - c1 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0); - c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI * 0.5); + c1 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0); + c2 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI * 0.5); b = new Bounds(); b = c1.getBounds(b); @@ -252,8 +252,8 @@ public class GeoBBoxTest { //assertEquals(-Math.PI,b.getLeftLongitude(),0.000001); //assertEquals(Math.PI*0.5,b.getRightLongitude(),0.000001); - c1 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI * 0.5, 0.0); - c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI); + c1 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI * 0.5, 0.0); + c2 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI); b = new Bounds(); b = c1.getBounds(b); diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java index 2e16be200c6..11e1ad1c735 100755 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java @@ -30,16 +30,16 @@ public class GeoCircleTest { public void testCircleDistance() { GeoCircle c; GeoPoint gp; - c = new GeoCircle(0.0, -0.5, 0.1); - gp = new GeoPoint(0.0, 0.0); + c = new GeoCircle(PlanetModel.SPHERE, 0.0, -0.5, 0.1); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0); assertEquals(Double.MAX_VALUE, c.computeArcDistance(gp), 0.0); assertEquals(Double.MAX_VALUE, c.computeLinearDistance(gp), 0.0); assertEquals(Double.MAX_VALUE, c.computeNormalDistance(gp), 0.0); - gp = new GeoPoint(0.0, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5); assertEquals(0.0, c.computeArcDistance(gp), 0.000001); assertEquals(0.0, c.computeLinearDistance(gp), 0.000001); assertEquals(0.0, c.computeNormalDistance(gp), 0.000001); - gp = new GeoPoint(0.05, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5); assertEquals(0.05, c.computeArcDistance(gp), 0.000001); assertEquals(0.049995, c.computeLinearDistance(gp), 0.000001); assertEquals(0.049979, c.computeNormalDistance(gp), 0.000001); @@ -49,18 +49,18 @@ public class GeoCircleTest { public void testCirclePointWithin() { GeoCircle c; GeoPoint gp; - c = new GeoCircle(0.0, -0.5, 0.1); - gp = new GeoPoint(0.0, 0.0); + c = new GeoCircle(PlanetModel.SPHERE, 0.0, -0.5, 0.1); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.55); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.45); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(Math.PI * 0.5, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.0); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0, Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI); assertFalse(c.isWithin(gp)); } @@ -71,7 +71,7 @@ public class GeoCircleTest { // Vertical circle cases - c = new GeoCircle(0.0, -0.5, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, 0.0, -0.5, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -80,7 +80,7 @@ public class GeoCircleTest { assertEquals(-0.4, b.getRightLongitude(), 0.000001); assertEquals(-0.1, b.getMinLatitude(), 0.000001); assertEquals(0.1, b.getMaxLatitude(), 0.000001); - c = new GeoCircle(0.0, 0.5, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, 0.0, 0.5, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -89,7 +89,7 @@ public class GeoCircleTest { assertEquals(0.6, b.getRightLongitude(), 0.000001); assertEquals(-0.1, b.getMinLatitude(), 0.000001); assertEquals(0.1, b.getMaxLatitude(), 0.000001); - c = new GeoCircle(0.0, 0.0, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -98,7 +98,7 @@ public class GeoCircleTest { assertEquals(0.1, b.getRightLongitude(), 0.000001); assertEquals(-0.1, b.getMinLatitude(), 0.000001); assertEquals(0.1, b.getMaxLatitude(), 0.000001); - c = new GeoCircle(0.0, Math.PI, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, 0.0, Math.PI, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -108,13 +108,13 @@ public class GeoCircleTest { assertEquals(-0.1, b.getMinLatitude(), 0.000001); assertEquals(0.1, b.getMaxLatitude(), 0.000001); // Horizontal circle cases - c = new GeoCircle(Math.PI * 0.5, 0.0, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, Math.PI * 0.5, 0.0, 0.1); b = c.getBounds(null); assertTrue(b.checkNoLongitudeBound()); assertTrue(b.checkNoTopLatitudeBound()); assertFalse(b.checkNoBottomLatitudeBound()); assertEquals(Math.PI * 0.5 - 0.1, b.getMinLatitude(), 0.000001); - c = new GeoCircle(-Math.PI * 0.5, 0.0, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0, 0.1); b = c.getBounds(null); assertTrue(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -122,7 +122,7 @@ public class GeoCircleTest { assertEquals(-Math.PI * 0.5 + 0.1, b.getMaxLatitude(), 0.000001); // Now do a somewhat tilted plane, facing different directions. - c = new GeoCircle(0.01, 0.0, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, 0.01, 0.0, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -132,7 +132,7 @@ public class GeoCircleTest { assertEquals(-0.1, b.getLeftLongitude(), 0.00001); assertEquals(0.1, b.getRightLongitude(), 0.00001); - c = new GeoCircle(0.01, Math.PI, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, 0.01, Math.PI, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -142,7 +142,7 @@ public class GeoCircleTest { assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.00001); assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.00001); - c = new GeoCircle(0.01, Math.PI * 0.5, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, 0.01, Math.PI * 0.5, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -152,7 +152,7 @@ public class GeoCircleTest { assertEquals(Math.PI * 0.5 - 0.1, b.getLeftLongitude(), 0.00001); assertEquals(Math.PI * 0.5 + 0.1, b.getRightLongitude(), 0.00001); - c = new GeoCircle(0.01, -Math.PI * 0.5, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, 0.01, -Math.PI * 0.5, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -163,7 +163,7 @@ public class GeoCircleTest { assertEquals(-Math.PI * 0.5 + 0.1, b.getRightLongitude(), 0.00001); // Slightly tilted, PI/4 direction. - c = new GeoCircle(0.01, Math.PI * 0.25, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, 0.01, Math.PI * 0.25, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -173,7 +173,7 @@ public class GeoCircleTest { assertEquals(Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001); assertEquals(Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001); - c = new GeoCircle(0.01, -Math.PI * 0.25, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, 0.01, -Math.PI * 0.25, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -183,7 +183,7 @@ public class GeoCircleTest { assertEquals(-Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001); assertEquals(-Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001); - c = new GeoCircle(-0.01, Math.PI * 0.25, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, -0.01, Math.PI * 0.25, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -193,7 +193,7 @@ public class GeoCircleTest { assertEquals(Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001); assertEquals(Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001); - c = new GeoCircle(-0.01, -Math.PI * 0.25, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, -0.01, -Math.PI * 0.25, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); @@ -204,7 +204,7 @@ public class GeoCircleTest { assertEquals(-Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001); // Now do a somewhat tilted plane. - c = new GeoCircle(0.01, -0.5, 0.1); + c = new GeoCircle(PlanetModel.SPHERE, 0.01, -0.5, 0.1); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); assertFalse(b.checkNoTopLatitudeBound()); diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java index 1df933e05c9..0bb7179821d 100755 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java @@ -30,37 +30,37 @@ public class GeoConvexPolygonTest { public void testPolygonPointWithin() { GeoConvexPolygon c; GeoPoint gp; - c = new GeoConvexPolygon(-0.1, -0.5); + c = new GeoConvexPolygon(PlanetModel.SPHERE, -0.1, -0.5); c.addPoint(0.0, -0.6, false); c.addPoint(0.1, -0.5, false); c.addPoint(0.0, -0.4, false); c.donePoints(false); // Sample some points within - gp = new GeoPoint(0.0, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.55); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.45); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(-0.05, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, -0.05, -0.5); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.05, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5); assertTrue(c.isWithin(gp)); // Sample some nearby points outside - gp = new GeoPoint(0.0, -0.65); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.65); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.35); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(-0.15, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, -0.15, -0.5); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.15, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.15, -0.5); assertFalse(c.isWithin(gp)); // Random points outside - gp = new GeoPoint(0.0, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(Math.PI * 0.5, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.0); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0, Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI); assertFalse(c.isWithin(gp)); } @@ -69,7 +69,7 @@ public class GeoConvexPolygonTest { GeoConvexPolygon c; Bounds b; - c = new GeoConvexPolygon(-0.1, -0.5); + c = new GeoConvexPolygon(PlanetModel.SPHERE, -0.1, -0.5); c.addPoint(0.0, -0.6, false); c.addPoint(0.1, -0.5, false); c.addPoint(0.0, -0.4, false); diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoModelTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoModelTest.java new file mode 100644 index 00000000000..4e294dfc7b4 --- /dev/null +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoModelTest.java @@ -0,0 +1,106 @@ +package org.apache.lucene.spatial.spatial4j.geo3d; + +/* + * 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. + */ + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test basic plane functionality. + */ +public class GeoModelTest { + + protected final static PlanetModel scaledModel = new PlanetModel(1.2,1.5); + + @Test + public void testBasicCircle() { + // The point of this test is just to make sure nothing blows up doing normal things with a quite non-spherical model + // Make sure that the north pole is in the circle, and south pole isn't + final GeoPoint northPole = new GeoPoint(scaledModel, Math.PI * 0.5, 0.0); + final GeoPoint southPole = new GeoPoint(scaledModel, -Math.PI * 0.5, 0.0); + final GeoPoint point1 = new GeoPoint(scaledModel, Math.PI * 0.25, 0.0); + final GeoPoint point2 = new GeoPoint(scaledModel, Math.PI * 0.125, 0.0); + + GeoCircle circle = new GeoCircle(scaledModel, Math.PI * 0.5, 0.0, 0.01); + assertTrue(circle.isWithin(northPole)); + assertFalse(circle.isWithin(southPole)); + assertFalse(circle.isWithin(point1)); + Bounds bounds = circle.getBounds(null); + assertTrue(bounds.checkNoLongitudeBound()); + assertTrue(bounds.checkNoTopLatitudeBound()); + assertFalse(bounds.checkNoBottomLatitudeBound()); + assertEquals(Math.PI * 0.5 - 0.01, bounds.getMinLatitude(), 0.01); + + circle = new GeoCircle(scaledModel, Math.PI * 0.25, 0.0, 0.01); + assertTrue(circle.isWithin(point1)); + assertFalse(circle.isWithin(northPole)); + assertFalse(circle.isWithin(southPole)); + bounds = circle.getBounds(null); + assertFalse(bounds.checkNoTopLatitudeBound()); + assertFalse(bounds.checkNoLongitudeBound()); + assertFalse(bounds.checkNoBottomLatitudeBound()); + assertEquals(1.20, bounds.getMaxLatitude(), 0.01); + assertEquals(Math.PI * 0.25 - 0.01, bounds.getMinLatitude(), 0.01); + assertEquals(-0.36, bounds.getLeftLongitude(), 0.01); + assertEquals(0.36, bounds.getRightLongitude(), 0.01); + + circle = new GeoCircle(scaledModel, Math.PI * 0.125, 0.0, 0.01); + assertTrue(circle.isWithin(point2)); + assertFalse(circle.isWithin(northPole)); + assertFalse(circle.isWithin(southPole)); + bounds = circle.getBounds(null); + assertFalse(bounds.checkNoLongitudeBound()); + assertFalse(bounds.checkNoTopLatitudeBound()); + assertFalse(bounds.checkNoBottomLatitudeBound()); + // Asymmetric, as expected + assertEquals(Math.PI * 0.125 - 0.01, bounds.getMinLatitude(), 0.01); + assertEquals(0.74, bounds.getMaxLatitude(), 0.01); + assertEquals(-0.18, bounds.getLeftLongitude(), 0.01); + assertEquals(0.18, bounds.getRightLongitude(), 0.01); + + } + + @Test + public void testBasicRectangle() { + final GeoBBox bbox = GeoBBoxFactory.makeGeoBBox(scaledModel, 1.0, 0.0, 0.0, 1.0); + final GeoPoint insidePoint = new GeoPoint(scaledModel, 0.5, 0.5); + assertTrue(bbox.isWithin(insidePoint)); + final GeoPoint topOutsidePoint = new GeoPoint(scaledModel, 1.01, 0.5); + assertFalse(bbox.isWithin(topOutsidePoint)); + final GeoPoint bottomOutsidePoint = new GeoPoint(scaledModel, -0.01, 0.5); + assertFalse(bbox.isWithin(bottomOutsidePoint)); + final GeoPoint leftOutsidePoint = new GeoPoint(scaledModel, 0.5, -0.01); + assertFalse(bbox.isWithin(leftOutsidePoint)); + final GeoPoint rightOutsidePoint = new GeoPoint(scaledModel, 0.5, 1.01); + assertFalse(bbox.isWithin(rightOutsidePoint)); + final Bounds bounds = bbox.getBounds(null); + assertFalse(bounds.checkNoLongitudeBound()); + assertFalse(bounds.checkNoTopLatitudeBound()); + assertFalse(bounds.checkNoBottomLatitudeBound()); + assertEquals(1.0, bounds.getMaxLatitude(), 0.00001); + assertEquals(0.0, bounds.getMinLatitude(), 0.00001); + assertEquals(1.0, bounds.getRightLongitude(), 0.00001); + assertEquals(0.0, bounds.getLeftLongitude(), 0.00001); + } + +} + + diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java index f6413ab545f..27b37925817 100755 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java @@ -30,47 +30,47 @@ public class GeoPathTest { // Start with a really simple case GeoPath p; GeoPoint gp; - p = new GeoPath(0.1); + p = new GeoPath(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(Math.PI * 0.5, 0.15); + gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.15); assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0); - gp = new GeoPoint(0.05, 0.15); + gp = new GeoPoint(PlanetModel.SPHERE, 0.05, 0.15); assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(0.0, 0.12); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.12); assertEquals(0.12 + 0.0, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(-0.15, 0.05); + gp = new GeoPoint(PlanetModel.SPHERE, -0.15, 0.05); assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(0.0, 0.25); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.25); assertEquals(0.20 + 0.05, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(0.0, -0.05); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.05); assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001); // Compute path distances now - p = new GeoPath(0.1); + p = new GeoPath(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(0.05, 0.15); + gp = new GeoPoint(PlanetModel.SPHERE, 0.05, 0.15); assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(0.0, 0.12); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.12); assertEquals(0.12, p.computeArcDistance(gp), 0.000001); // Now try a vertical path, and make sure distances are as expected - p = new GeoPath(0.1); + p = new GeoPath(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(0.0, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0); assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0); - gp = new GeoPoint(-0.1, -1.0); + gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.0); assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0); - gp = new GeoPoint(Math.PI * 0.25 + 0.05, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.25 + 0.05, -0.5); assertEquals(Math.PI * 0.5 + 0.05, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(-Math.PI * 0.25 - 0.05, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.25 - 0.05, -0.5); assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001); } @@ -79,47 +79,48 @@ public class GeoPathTest { // Tests whether we can properly detect whether a point is within a path or not GeoPath p; GeoPoint gp; - p = new GeoPath(0.1); + p = new GeoPath(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(-0.2, -0.2); + gp = new GeoPoint(PlanetModel.SPHERE, -0.2, -0.2); assertTrue(p.isWithin(gp)); - gp = new GeoPoint(0.0, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0); assertTrue(p.isWithin(gp)); - gp = new GeoPoint(0.1, 0.1); + gp = new GeoPoint(PlanetModel.SPHERE, 0.1, 0.1); assertTrue(p.isWithin(gp)); // Test points off the path - gp = new GeoPoint(-0.2, 0.2); + gp = new GeoPoint(PlanetModel.SPHERE, -0.2, 0.2); assertFalse(p.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.5, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0); assertFalse(p.isWithin(gp)); - gp = new GeoPoint(0.2, -0.2); + gp = new GeoPoint(PlanetModel.SPHERE, 0.2, -0.2); assertFalse(p.isWithin(gp)); - gp = new GeoPoint(0.0, Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI); assertFalse(p.isWithin(gp)); // Repeat the test, but across the terminator - p = new GeoPath(0.1); + p = new GeoPath(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(-0.2, Math.PI - 0.2); + gp = new GeoPoint(PlanetModel.SPHERE, -0.2, Math.PI - 0.2); assertTrue(p.isWithin(gp)); - gp = new GeoPoint(0.0, Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI); assertTrue(p.isWithin(gp)); - gp = new GeoPoint(0.1, -Math.PI + 0.1); + gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI + 0.1); assertTrue(p.isWithin(gp)); // Test points off the path - gp = new GeoPoint(-0.2, -Math.PI + 0.2); + gp = new GeoPoint(PlanetModel.SPHERE, -0.2, -Math.PI + 0.2); assertFalse(p.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.5, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0); assertFalse(p.isWithin(gp)); - gp = new GeoPoint(0.2, Math.PI - 0.2); + gp = new GeoPoint(PlanetModel.SPHERE, 0.2, Math.PI - 0.2); assertFalse(p.isWithin(gp)); - gp = new GeoPoint(0.0, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0); assertFalse(p.isWithin(gp)); } @@ -131,31 +132,31 @@ public class GeoPathTest { // Start by testing the basic kinds of relationship, increasing in order of difficulty. - p = new GeoPath(0.1); + p = new GeoPath(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(0.05, -0.05, -0.05, 0.05); + 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(0.05, -0.05, -0.05, 0.5); + 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(0.05, -0.05, -0.5, 0.5); + 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(0.5, -0.5, -0.5, 0.5); + 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(0.1, 0.0, -0.1, 0.0); + rect = new GeoRectangle(PlanetModel.SPHERE, 0.1, 0.0, -0.1, 0.0); assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p)); - rect = new GeoRectangle(0.2, 0.1, -0.2, -0.1); + 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(0.5, -0.5, -0.5, -0.35); + rect = new GeoRectangle(PlanetModel.SPHERE, 0.5, -0.5, -0.5, -0.35); assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p)); - rect = new GeoRectangle(0.5, -0.5, -0.5, -0.45); + rect = new GeoRectangle(PlanetModel.SPHERE, 0.5, -0.5, -0.5, -0.45); assertEquals(GeoArea.DISJOINT, rect.getRelationship(p)); } @@ -165,7 +166,7 @@ public class GeoPathTest { GeoPath c; Bounds b; - c = new GeoPath(0.1); + c = new GeoPath(PlanetModel.SPHERE, 0.1); c.addPoint(-0.3, -0.3); c.addPoint(0.3, 0.3); c.done(); diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonTest.java index 87b26e8f0d5..b44ee2bd2fd 100755 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonTest.java @@ -36,49 +36,49 @@ public class GeoPolygonTest { List points; points = new ArrayList(); - points.add(new GeoPoint(-0.1, -0.5)); - points.add(new GeoPoint(0.0, -0.6)); - points.add(new GeoPoint(0.1, -0.5)); - points.add(new GeoPoint(0.0, -0.4)); + points.add(new GeoPoint(PlanetModel.SPHERE, -0.1, -0.5)); + points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.6)); + points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.5)); + points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4)); - c = GeoPolygonFactory.makeGeoPolygon(points, 0); + c = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points, 0); // Sample some points within - gp = new GeoPoint(0.0, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.55); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.45); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(-0.05, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, -0.05, -0.5); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.05, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5); assertTrue(c.isWithin(gp)); // Sample some nearby points outside - gp = new GeoPoint(0.0, -0.65); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.65); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.35); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(-0.15, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, -0.15, -0.5); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.15, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.15, -0.5); assertFalse(c.isWithin(gp)); // Random points outside - gp = new GeoPoint(0.0, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(Math.PI * 0.5, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.0); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0, Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI); assertFalse(c.isWithin(gp)); points = new ArrayList(); - points.add(new GeoPoint(-0.1, -0.5)); - points.add(new GeoPoint(-0.01, -0.6)); - points.add(new GeoPoint(-0.1, -0.7)); - points.add(new GeoPoint(0.0, -0.8)); - points.add(new GeoPoint(0.1, -0.7)); - points.add(new GeoPoint(0.01, -0.6)); - points.add(new GeoPoint(0.1, -0.5)); - points.add(new GeoPoint(0.0, -0.4)); + points.add(new GeoPoint(PlanetModel.SPHERE, -0.1, -0.5)); + points.add(new GeoPoint(PlanetModel.SPHERE, -0.01, -0.6)); + points.add(new GeoPoint(PlanetModel.SPHERE, -0.1, -0.7)); + points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.8)); + points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.7)); + points.add(new GeoPoint(PlanetModel.SPHERE, 0.01, -0.6)); + points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.5)); + points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4)); /* System.out.println("Points: "); @@ -87,33 +87,33 @@ public class GeoPolygonTest { } */ - c = GeoPolygonFactory.makeGeoPolygon(points, 0); + c = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points, 0); // Sample some points within - gp = new GeoPoint(0.0, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.55); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.45); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(-0.05, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, -0.05, -0.5); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.05, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5); assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0, -0.7); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.7); assertTrue(c.isWithin(gp)); // Sample some nearby points outside - gp = new GeoPoint(0.0, -0.35); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(-0.15, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, -0.15, -0.5); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.15, -0.5); + gp = new GeoPoint(PlanetModel.SPHERE, 0.15, -0.5); assertFalse(c.isWithin(gp)); // Random points outside - gp = new GeoPoint(0.0, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(Math.PI * 0.5, 0.0); + gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.0); assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0, Math.PI); + gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI); assertFalse(c.isWithin(gp)); } @@ -125,12 +125,12 @@ public class GeoPolygonTest { List points; points = new ArrayList(); - points.add(new GeoPoint(-0.1, -0.5)); - points.add(new GeoPoint(0.0, -0.6)); - points.add(new GeoPoint(0.1, -0.5)); - points.add(new GeoPoint(0.0, -0.4)); + points.add(new GeoPoint(PlanetModel.SPHERE, -0.1, -0.5)); + points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.6)); + points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.5)); + points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4)); - c = GeoPolygonFactory.makeGeoPolygon(points, 0); + c = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points, 0); b = c.getBounds(null); assertFalse(b.checkNoLongitudeBound()); diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/PlaneTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/PlaneTest.java index 5020f6e0efc..a3337373b60 100644 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/PlaneTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/PlaneTest.java @@ -30,7 +30,7 @@ public class PlaneTest { @Test public void testIdenticalPlanes() { - final GeoPoint p = new GeoPoint(0.123, -0.456); + final GeoPoint p = new GeoPoint(PlanetModel.SPHERE, 0.123, -0.456); final Plane plane1 = new Plane(p, 0.0); final Plane plane2 = new Plane(p, 0.0); assertTrue(plane1.isNumericallyIdentical(plane2)); From 0a00734a60938f5705e2fad1605c291ecb592bf2 Mon Sep 17 00:00:00 2001 From: David Wayne Smiley Date: Wed, 27 May 2015 13:34:52 +0000 Subject: [PATCH 3/6] LUCENE-6487: Geo3D with WGS84 patch from Karl: GeoPoint.getLat & getLon. git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene6487@1682021 13f79535-47bb-0310-9956-ffa450edef68 --- .../spatial/spatial4j/geo3d/GeoPoint.java | 51 ++++++++++++++++++- .../lucene/spatial/spatial4j/geo3d/Plane.java | 2 +- .../spatial/spatial4j/geo3d/Vector.java | 20 +++++--- .../Geo3dShapeRectRelationTestCase.java | 4 +- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java index 67844371359..335c6db877c 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java @@ -24,30 +24,79 @@ package org.apache.lucene.spatial.spatial4j.geo3d; */ public class GeoPoint extends Vector { + /** This is the lazily-evaluated magnitude. Some constructors include it, but others don't, and + * we try not to create extra computation by always computing it. */ protected double magnitude = Double.NEGATIVE_INFINITY; + /** Construct a GeoPoint from the trig functions of a lat and lon pair. + * @param planetModel is the planetModel to put the point on. + * @param sinLat is the sin of the latitude. + * @param sinLon is the sin of the longitude. + * @param cosLat is the cos of the latitude. + * @param cosLon is the cos of the longitude. + */ public GeoPoint(final PlanetModel planetModel, final double sinLat, final double sinLon, final double cosLat, final double cosLon) { - this(computeMagnitude(planetModel, cosLat * cosLon, cosLat * sinLon, sinLat), + this(computeDesiredEllipsoidMagnitude(planetModel, cosLat * cosLon, cosLat * sinLon, sinLat), cosLat * cosLon, cosLat * sinLon, sinLat); } + /** Construct a GeoPoint from a latitude/longitude pair. + * @param planetModel is the planetModel to put the point on. + * @param lat is the latitude. + * @param lon is the longitude. + */ public GeoPoint(final PlanetModel planetModel, final double lat, final double lon) { this(planetModel, Math.sin(lat), Math.sin(lon), Math.cos(lat), Math.cos(lon)); } + /** Construct a GeoPoint from a unit (x,y,z) vector and a magnitude. + * @param magnitude is the desired magnitude, provided to put the point on the ellipsoid. + * @param x is the unit x value. + * @param y is the unit y value. + * @param z is the unit z value. + */ public GeoPoint(final double magnitude, final double x, final double y, final double z) { super(x * magnitude, y * magnitude, z * magnitude); this.magnitude = magnitude; } + /** Construct a GeoPoint from an (x,y,z) value. + * The (x,y,z) tuple must be on the desired ellipsoid. + * @param x is the ellipsoid point x value. + * @param y is the ellipsoid point y value. + * @param z is the ellipsoid point z value. + */ public GeoPoint(final double x, final double y, final double z) { super(x, y, z); } + /** Compute an arc distance between two points. + * @param v is the second point. + * @return the angle, in radians, between the two points. + */ public double arcDistance(final GeoPoint v) { return Tools.safeAcos(dotProduct(v)/(magnitude() * v.magnitude())); } + /** Compute the latitude for the point. + *@return the latitude. + */ + public double getLatitude() { + return Math.asin(z / magnitude() ); + } + + /** Compute the longitude for the point. + * @return the longitude value. Uses 0.0 if there is no computable longitude. + */ + public double getLongitude() { + if (Math.abs(x) < MINIMUM_RESOLUTION && Math.abs(y) < MINIMUM_RESOLUTION) + return 0.0; + return Math.atan2(y,z); + } + + /** Compute the linear magnitude of the point. + * @return the magnitude. + */ @Override public double magnitude() { if (this.magnitude == Double.NEGATIVE_INFINITY) { diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java index 95f19a9c15f..b5f79dfdc17 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java @@ -56,7 +56,7 @@ public class Plane extends Vector { */ public Plane(final PlanetModel planetModel, final double sinLat) { super(0.0, 0.0, 1.0); - D = -sinLat * computeMagnitude(planetModel, sinLat); + D = -sinLat * computeDesiredEllipsoidMagnitude(planetModel, sinLat); } /** diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java index fe84c05611f..a29249f4c72 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java @@ -307,20 +307,28 @@ public class Vector { return Math.sqrt(x * x + y * y + z * z); } - /** Compute the magnitude of a vector projected to a given + /** Compute the desired magnitude of a unit vector projected to a given * planet model. + * @param planetModel is the planet model. + * @param x is the unit vector x value. + * @param y is the unit vector y value. + * @param z is the unit vector z value. + * @return a magnitude value for that (x,y,z) that projects the vector onto the specified ellipsoid. */ - protected static double computeMagnitude(final PlanetModel planetModel, final double x, final double y, final double z) { + protected static double computeDesiredEllipsoidMagnitude(final PlanetModel planetModel, final double x, final double y, final double z) { return 1.0 / Math.sqrt(x*x*planetModel.inverseAbSquared + y*y*planetModel.inverseAbSquared + z*z*planetModel.inverseCSquared); } - /** Compute the magnitude of a vector projected to a given - * planet model. + /** Compute the desired magnitude of a unit vector projected to a given + * planet model. The unit vector is specified only by a z value. + * @param planetModel is the planet model. + * @param z is the unit vector z value. + * @return a magnitude value for that z value that projects the vector onto the specified ellipsoid. */ - protected static double computeMagnitude(final PlanetModel planetModel, final double z) { + protected static double computeDesiredEllipsoidMagnitude(final PlanetModel planetModel, final double z) { return 1.0 / Math.sqrt((1.0-z*z)*planetModel.inverseAbSquared + z*z*planetModel.inverseCSquared); } - + @Override public boolean equals(Object o) { if (!(o instanceof Vector)) diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java index 2755d848667..14b117288c3 100644 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java @@ -237,7 +237,7 @@ public abstract class Geo3dShapeRectRelationTestCase extends RandomizedShapeTest } private Point geoPointToSpatial4jPoint(GeoPoint geoPoint) { - return ctx.makePoint(geoPoint.x * DistanceUtils.RADIANS_TO_DEGREES, - geoPoint.y * DistanceUtils.RADIANS_TO_DEGREES); + return ctx.makePoint(geoPoint.getLongitude() * DistanceUtils.RADIANS_TO_DEGREES, + geoPoint.getLongitude() * DistanceUtils.RADIANS_TO_DEGREES); } } From 97a5295f075d37b1a31c5e77e85f7a9934cae096 Mon Sep 17 00:00:00 2001 From: David Wayne Smiley Date: Fri, 29 May 2015 00:20:01 +0000 Subject: [PATCH 4/6] LUCENE-6487: Geo3D with WGS84 patch from Karl: fix bug in GeoPoint.getLongitude with test from https://reviews.apache.org/r/34744/diff/raw/ git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene6487@1682357 13f79535-47bb-0310-9956-ffa450edef68 --- .../spatial/spatial4j/geo3d/GeoPoint.java | 2 +- .../spatial/spatial4j/geo3d/GeoPointTest.java | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java index 335c6db877c..520db18d9cf 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java @@ -91,7 +91,7 @@ public class GeoPoint extends Vector { public double getLongitude() { if (Math.abs(x) < MINIMUM_RESOLUTION && Math.abs(y) < MINIMUM_RESOLUTION) return 0.0; - return Math.atan2(y,z); + return Math.atan2(y,x); } /** Compute the linear magnitude of the point. diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java new file mode 100644 index 00000000000..71fc9a3fdce --- /dev/null +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java @@ -0,0 +1,44 @@ +package org.apache.lucene.spatial.spatial4j.geo3d; + +/* + * 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. + */ + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Test basic GeoPoint functionality. + */ +public class GeoPointTest { + + @Test + public void testConversion() { + final double pLat = 0.123; + final double pLon = -0.456; + final GeoPoint p1 = new GeoPoint(PlanetModel.SPHERE, pLat, pLon); + assertEquals(pLat, p1.getLatitude(), 1e-12); + assertEquals(pLon, p1.getLongitude(), 1e-12); + final GeoPoint p2 = new GeoPoint(PlanetModel.WGS84, pLat, pLon); + assertEquals(pLat, p2.getLatitude(), 1e-12); + assertEquals(pLon, p2.getLongitude(), 1e-12); + + } + +} + + From e8b5e3242dff79b8282d37c108612bb0fac486d5 Mon Sep 17 00:00:00 2001 From: David Wayne Smiley Date: Fri, 29 May 2015 02:07:55 +0000 Subject: [PATCH 5/6] LUCENE-6487: Geo3D with WGS84: randomize GeoPointTest lat-lon round-trip git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene6487@1682359 13f79535-47bb-0310-9956-ffa450edef68 --- .../lucene/spatial/spatial4j/geo3d/GeoPointTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java index 71fc9a3fdce..3dbdb2fdfb2 100644 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java @@ -17,19 +17,21 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ +import com.spatial4j.core.distance.DistanceUtils; +import org.apache.lucene.util.LuceneTestCase; import org.junit.Test; -import static org.junit.Assert.assertEquals; +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomFloat; /** * Test basic GeoPoint functionality. */ -public class GeoPointTest { +public class GeoPointTest extends LuceneTestCase { @Test public void testConversion() { - final double pLat = 0.123; - final double pLon = -0.456; + final double pLat = (randomFloat() * 180.0 - 90.0) * DistanceUtils.DEGREES_TO_RADIANS; + final double pLon = (randomFloat() * 360.0 - 180.0) * DistanceUtils.DEGREES_TO_RADIANS; final GeoPoint p1 = new GeoPoint(PlanetModel.SPHERE, pLat, pLon); assertEquals(pLat, p1.getLatitude(), 1e-12); assertEquals(pLon, p1.getLongitude(), 1e-12); From 64a34575d3744f2b29bb0b7bc288dc7668642864 Mon Sep 17 00:00:00 2001 From: David Wayne Smiley Date: Sun, 31 May 2015 02:50:21 +0000 Subject: [PATCH 6/6] LUCENE-6487: Geo3D with WGS84: fix GeoPointTest to test via distance git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene6487@1682667 13f79535-47bb-0310-9956-ffa450edef68 --- .../spatial/spatial4j/geo3d/GeoPointTest.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java index 3dbdb2fdfb2..1b59825efd9 100644 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java @@ -30,15 +30,25 @@ public class GeoPointTest extends LuceneTestCase { @Test public void testConversion() { - final double pLat = (randomFloat() * 180.0 - 90.0) * DistanceUtils.DEGREES_TO_RADIANS; - final double pLon = (randomFloat() * 360.0 - 180.0) * DistanceUtils.DEGREES_TO_RADIANS; - final GeoPoint p1 = new GeoPoint(PlanetModel.SPHERE, pLat, pLon); - assertEquals(pLat, p1.getLatitude(), 1e-12); - assertEquals(pLon, p1.getLongitude(), 1e-12); - final GeoPoint p2 = new GeoPoint(PlanetModel.WGS84, pLat, pLon); - assertEquals(pLat, p2.getLatitude(), 1e-12); - assertEquals(pLon, p2.getLongitude(), 1e-12); - + testPointRoundTrip(PlanetModel.SPHERE, 90, 0, 1e-12); + testPointRoundTrip(PlanetModel.SPHERE, -90, 0, 1e-12); + testPointRoundTrip(PlanetModel.WGS84, 90, 0, 1e-12); + testPointRoundTrip(PlanetModel.WGS84, -90, 0, 1e-12); + + final int times = atLeast(100); + for (int i = 0; i < times; i++) { + final double pLat = (randomFloat() * 180.0 - 90.0) * DistanceUtils.DEGREES_TO_RADIANS; + final double pLon = (randomFloat() * 360.0 - 180.0) * DistanceUtils.DEGREES_TO_RADIANS; + testPointRoundTrip(PlanetModel.SPHERE, pLat, pLon, 1e-6);//1e-6 since there's a square root in there (Karl says) + testPointRoundTrip(PlanetModel.WGS84, pLat, pLon, 1e-6); + } + } + + protected void testPointRoundTrip(PlanetModel planetModel, double pLat, double pLon, double epsilon) { + final GeoPoint p1 = new GeoPoint(planetModel, pLat, pLon); + final GeoPoint p2 = new GeoPoint(planetModel, p1.getLatitude(), p1.getLongitude()); + double dist = p1.arcDistance(p2); + assertEquals(0, dist, epsilon); } }