You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2016/03/01 18:07:02 UTC
[19/50] [abbrv] lucene-solr git commit: LUCENE-7015: Refactor spatial
module to spatial-extras
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial-extras/src/test/org/apache/lucene/spatial/serialized/SerializedStrategyTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/serialized/SerializedStrategyTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/serialized/SerializedStrategyTest.java
new file mode 100644
index 0000000..bed8339
--- /dev/null
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/serialized/SerializedStrategyTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.spatial.serialized;
+
+import java.io.IOException;
+
+import com.spatial4j.core.context.SpatialContext;
+import org.apache.lucene.spatial.SpatialMatchConcern;
+import org.apache.lucene.spatial.StrategyTestCase;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SerializedStrategyTest extends StrategyTestCase {
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ this.ctx = SpatialContext.GEO;
+ this.strategy = new SerializedDVStrategy(ctx, "serialized");
+ }
+
+ @Override
+ protected boolean needsDocValues() {
+ return (strategy instanceof SerializedDVStrategy);
+ }
+
+ @Test
+ public void testBasicOperaions() throws IOException {
+ getAddAndVerifyIndexedDocuments(DATA_SIMPLE_BBOX);
+
+ executeQueries(SpatialMatchConcern.EXACT, QTEST_Simple_Queries_BBox);
+ }
+
+ @Test
+ public void testStatesBBox() throws IOException {
+ getAddAndVerifyIndexedDocuments(DATA_STATES_BBOX);
+
+ executeQueries(SpatialMatchConcern.FILTER, QTEST_States_IsWithin_BBox);
+ executeQueries(SpatialMatchConcern.FILTER, QTEST_States_Intersects_BBox);
+ }
+
+ @Test
+ public void testCitiesIntersectsBBox() throws IOException {
+ getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS);
+
+ executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_Intersects_BBox);
+ }
+
+ //sorting is tested in DistanceStrategyTest
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java
new file mode 100644
index 0000000..8040a35
--- /dev/null
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.spatial.spatial4j;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.carrotsearch.randomizedtesting.annotations.Repeat;
+import com.spatial4j.core.context.SpatialContext;
+import com.spatial4j.core.shape.Point;
+import com.spatial4j.core.shape.Rectangle;
+import com.spatial4j.core.shape.Shape;
+import org.apache.lucene.spatial.composite.CompositeSpatialStrategy;
+import org.apache.lucene.spatial.prefix.RandomSpatialOpStrategyTestCase;
+import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
+import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
+import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
+import org.apache.lucene.spatial.query.SpatialOperation;
+import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
+import org.apache.lucene.geo3d.GeoBBoxFactory;
+import org.apache.lucene.geo3d.GeoStandardCircle;
+import org.apache.lucene.geo3d.GeoPath;
+import org.apache.lucene.geo3d.GeoPoint;
+import org.apache.lucene.geo3d.GeoPolygonFactory;
+import org.apache.lucene.geo3d.GeoShape;
+import org.apache.lucene.geo3d.PlanetModel;
+import org.junit.Test;
+
+import static com.spatial4j.core.distance.DistanceUtils.DEGREES_TO_RADIANS;
+
+public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
+
+ private SpatialPrefixTree grid;
+ private RecursivePrefixTreeStrategy rptStrategy;
+ {
+ this.ctx = SpatialContext.GEO;
+ }
+
+ private void setupGeohashGrid() {
+ this.grid = new GeohashPrefixTree(ctx, 2);//A fairly shallow grid
+ this.rptStrategy = newRPT();
+ }
+
+ protected RecursivePrefixTreeStrategy newRPT() {
+ final RecursivePrefixTreeStrategy rpt = new RecursivePrefixTreeStrategy(this.grid,
+ getClass().getSimpleName() + "_rpt");
+ rpt.setDistErrPct(0.10);//not too many cells
+ return rpt;
+ }
+
+ @Override
+ protected boolean needsDocValues() {
+ return true;//due to SerializedDVStrategy
+ }
+
+ private void setupStrategy() {
+ //setup
+ setupGeohashGrid();
+
+ SerializedDVStrategy serializedDVStrategy = new SerializedDVStrategy(ctx, getClass().getSimpleName() + "_sdv");
+ this.strategy = new CompositeSpatialStrategy("composite_" + getClass().getSimpleName(),
+ rptStrategy, serializedDVStrategy);
+ }
+
+ @Test
+ public void testFailure1() throws IOException {
+ setupStrategy();
+ final List<GeoPoint> points = new ArrayList<GeoPoint>();
+ 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(PlanetModel.SPHERE, points,0),ctx);
+ final Rectangle rect = ctx.makeRectangle(-49, -45, 73, 86);
+ testOperation(rect,SpatialOperation.Intersects,triangle, false);
+ }
+
+ @Test
+ public void testFailureLucene6535() throws IOException {
+ setupStrategy();
+
+ final List<GeoPoint> points = new ArrayList<>();
+ 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 GeoPath path = new GeoPath(PlanetModel.SPHERE, 29 * DEGREES_TO_RADIANS);
+ path.addPoint(55.0 * DEGREES_TO_RADIANS, -26.0 * DEGREES_TO_RADIANS);
+ path.addPoint(-90.0 * DEGREES_TO_RADIANS, 0.0);
+ path.addPoint(54.0 * DEGREES_TO_RADIANS, 165.0 * DEGREES_TO_RADIANS);
+ path.addPoint(-90.0 * DEGREES_TO_RADIANS, 0.0);
+ path.done();
+ final Shape shape = new Geo3dShape(path,ctx);
+ final Rectangle rect = ctx.makeRectangle(131, 143, 39, 54);
+ testOperation(rect,SpatialOperation.Intersects,shape,true);
+ }
+
+ @Test
+ @Repeat(iterations = 10)
+ public void testOperations() throws IOException {
+ setupStrategy();
+
+ testOperationRandomShapes(SpatialOperation.Intersects);
+ }
+
+ private Shape makeTriangle(double x1, double y1, double x2, double y2, double x3, double y3) {
+ final List<GeoPoint> geoPoints = new ArrayList<>();
+ 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(PlanetModel.SPHERE, geoPoints, convexPointIndex);
+ return new Geo3dShape(shape, ctx);
+ }
+
+ @Override
+ protected Shape randomIndexedShape() {
+ return randomRectangle();
+ }
+
+ @Override
+ protected Shape randomQueryShape() {
+ final int shapeType = random().nextInt(4);
+ switch (shapeType) {
+ case 0: {
+ // Polygons
+ final int vertexCount = random().nextInt(3) + 3;
+ while (true) {
+ final List<GeoPoint> geoPoints = new ArrayList<>();
+ while (geoPoints.size() < vertexCount) {
+ final Point point = randomPoint();
+ 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(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
+ // the exception is thrown incorrectly, we aren't going to be able to do that in this random test.
+ continue;
+ }
+ }
+ }
+ case 1: {
+ // Circles
+ while (true) {
+ final int circleRadius = random().nextInt(179) + 1;
+ final Point point = randomPoint();
+ try {
+ final GeoShape shape = new GeoStandardCircle(PlanetModel.SPHERE, point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS,
+ circleRadius * DEGREES_TO_RADIANS);
+ 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
+ // the exception is thrown incorrectly, we aren't going to be able to do that in this random test.
+ continue;
+ }
+ }
+ }
+ case 2: {
+ // Rectangles
+ while (true) {
+ Point ulhcPoint = randomPoint();
+ Point lrhcPoint = randomPoint();
+ if (ulhcPoint.getY() < lrhcPoint.getY()) {
+ //swap
+ Point temp = ulhcPoint;
+ ulhcPoint = lrhcPoint;
+ lrhcPoint = temp;
+ }
+ try {
+ 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);
+ //System.err.println("Trial rectangle shape: "+shape);
+ 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
+ // the exception is thrown incorrectly, we aren't going to be able to do that in this random test.
+ continue;
+ }
+ }
+ }
+ case 3: {
+ // Paths
+ final int pointCount = random().nextInt(5) + 1;
+ final double width = (random().nextInt(89)+1) * DEGREES_TO_RADIANS;
+ while (true) {
+ try {
+ 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);
+ }
+ path.done();
+ return new Geo3dShape(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.
+ continue;
+ }
+ }
+ }
+ default:
+ throw new IllegalStateException("Unexpected shape type");
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java
new file mode 100644
index 0000000..58b520d
--- /dev/null
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java
@@ -0,0 +1,262 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.spatial.spatial4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.spatial4j.core.TestLog;
+import com.spatial4j.core.context.SpatialContext;
+import com.spatial4j.core.distance.DistanceUtils;
+import com.spatial4j.core.shape.Circle;
+import com.spatial4j.core.shape.Point;
+import com.spatial4j.core.shape.RectIntersectionTestHelper;
+import org.apache.lucene.geo3d.LatLonBounds;
+import org.apache.lucene.geo3d.GeoBBox;
+import org.apache.lucene.geo3d.GeoBBoxFactory;
+import org.apache.lucene.geo3d.GeoStandardCircle;
+import org.apache.lucene.geo3d.GeoPath;
+import org.apache.lucene.geo3d.GeoPoint;
+import org.apache.lucene.geo3d.GeoPolygonFactory;
+import org.apache.lucene.geo3d.GeoShape;
+import org.apache.lucene.geo3d.PlanetModel;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static com.spatial4j.core.distance.DistanceUtils.DEGREES_TO_RADIANS;
+
+public abstract class Geo3dShapeRectRelationTestCase extends RandomizedShapeTestCase {
+ protected final static double RADIANS_PER_DEGREE = Math.PI/180.0;
+
+ @Rule
+ public final TestLog testLog = TestLog.instance;
+
+ protected final PlanetModel planetModel;
+
+ public Geo3dShapeRectRelationTestCase(PlanetModel planetModel) {
+ super(SpatialContext.GEO);
+ this.planetModel = planetModel;
+ }
+
+ protected GeoBBox getBoundingBox(final GeoShape path) {
+ LatLonBounds bounds = new LatLonBounds();
+ path.getBounds(bounds);
+
+ double leftLon;
+ double rightLon;
+ if (bounds.checkNoLongitudeBound()) {
+ leftLon = -Math.PI;
+ rightLon = Math.PI;
+ } else {
+ leftLon = bounds.getLeftLongitude().doubleValue();
+ rightLon = bounds.getRightLongitude().doubleValue();
+ }
+ double minLat;
+ if (bounds.checkNoBottomLatitudeBound()) {
+ minLat = -Math.PI * 0.5;
+ } else {
+ minLat = bounds.getMinLatitude().doubleValue();
+ }
+ double maxLat;
+ if (bounds.checkNoTopLatitudeBound()) {
+ maxLat = Math.PI * 0.5;
+ } else {
+ maxLat = bounds.getMaxLatitude().doubleValue();
+ }
+ return GeoBBoxFactory.makeGeoBBox(planetModel, maxLat, minLat, leftLon, rightLon);
+ }
+
+ abstract class Geo3dRectIntersectionTestHelper extends RectIntersectionTestHelper<Geo3dShape> {
+
+ public Geo3dRectIntersectionTestHelper(SpatialContext ctx) {
+ super(ctx);
+ }
+
+ //20 times each -- should be plenty
+
+ protected int getContainsMinimum(int laps) {
+ return 20;
+ }
+
+ protected int getIntersectsMinimum(int laps) {
+ return 20;
+ }
+
+ // producing "within" cases in Geo3D based on our random shapes doesn't happen often. It'd be nice to increase this.
+ protected int getWithinMinimum(int laps) {
+ return 2;
+ }
+
+ protected int getDisjointMinimum(int laps) {
+ return 20;
+ }
+
+ protected int getBoundingMinimum(int laps) {
+ return 20;
+ }
+ }
+
+ @AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/LUCENE-6867")
+ @Test
+ public void testGeoCircleRect() {
+ new Geo3dRectIntersectionTestHelper(ctx) {
+
+ @Override
+ protected Geo3dShape generateRandomShape(Point nearP) {
+ final int circleRadius = 180 - random().nextInt(180);//no 0-radius
+ final Point point = nearP;
+ final GeoShape shape = new GeoStandardCircle(planetModel, point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS,
+ circleRadius * DEGREES_TO_RADIANS);
+ return new Geo3dShape(planetModel, shape, ctx);
+ }
+
+ @Override
+ protected Point randomPointInEmptyShape(Geo3dShape shape) {
+ GeoPoint geoPoint = ((GeoStandardCircle)shape.shape).getCenter();
+ return geoPointToSpatial4jPoint(geoPoint);
+ }
+
+ }.testRelateWithRectangle();
+ }
+
+ @AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/LUCENE-6867")
+ @Test
+ public void testGeoBBoxRect() {
+ new Geo3dRectIntersectionTestHelper(ctx) {
+
+ @Override
+ protected boolean isRandomShapeRectangular() {
+ return true;
+ }
+
+ @Override
+ protected Geo3dShape generateRandomShape(Point nearP) {
+ // (ignoring nearP)
+ Point ulhcPoint = randomPoint();
+ Point lrhcPoint = randomPoint();
+ if (ulhcPoint.getY() < lrhcPoint.getY()) {
+ //swap
+ Point temp = ulhcPoint;
+ ulhcPoint = lrhcPoint;
+ lrhcPoint = temp;
+ }
+ 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(planetModel, shape, ctx);
+ }
+
+ @Override
+ protected Point randomPointInEmptyShape(Geo3dShape shape) {
+ return shape.getBoundingBox().getCenter();
+ }
+ }.testRelateWithRectangle();
+ }
+
+ @AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/LUCENE-6867")
+ @Test
+ public void testGeoPolygonRect() {
+ new Geo3dRectIntersectionTestHelper(ctx) {
+
+ @Override
+ protected Geo3dShape generateRandomShape(Point nearP) {
+ final Point centerPoint = randomPoint();
+ final int maxDistance = random().nextInt(160) + 20;
+ final Circle pointZone = ctx.makeCircle(centerPoint, maxDistance);
+ final int vertexCount = random().nextInt(3) + 3;
+ while (true) {
+ final List<GeoPoint> geoPoints = new ArrayList<>();
+ while (geoPoints.size() < vertexCount) {
+ final Point point = randomPointIn(pointZone);
+ 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(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.
+ continue;
+ }
+ }
+ }
+
+ @Override
+ protected Point randomPointInEmptyShape(Geo3dShape shape) {
+ throw new IllegalStateException("unexpected; need to finish test code");
+ }
+
+ @Override
+ protected int getWithinMinimum(int laps) {
+ // Long/thin so lets just find 1.
+ return 1;
+ }
+
+ }.testRelateWithRectangle();
+ }
+
+ @AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/LUCENE-6867")
+ @Test
+ public void testGeoPathRect() {
+ new Geo3dRectIntersectionTestHelper(ctx) {
+
+ @Override
+ protected Geo3dShape generateRandomShape(Point nearP) {
+ final Point centerPoint = randomPoint();
+ final int maxDistance = random().nextInt(160) + 20;
+ final Circle pointZone = ctx.makeCircle(centerPoint, maxDistance);
+ final int pointCount = random().nextInt(5) + 1;
+ final double width = (random().nextInt(89)+1) * DEGREES_TO_RADIANS;
+ while (true) {
+ try {
+ final GeoPath path = new GeoPath(planetModel, width);
+ for (int i = 0; i < pointCount; i++) {
+ final Point nextPoint = randomPointIn(pointZone);
+ path.addPoint(nextPoint.getY() * DEGREES_TO_RADIANS, nextPoint.getX() * DEGREES_TO_RADIANS);
+ }
+ path.done();
+ 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.
+ continue;
+ }
+ }
+ }
+
+ @Override
+ protected Point randomPointInEmptyShape(Geo3dShape shape) {
+ throw new IllegalStateException("unexpected; need to finish test code");
+ }
+
+ @Override
+ protected int getWithinMinimum(int laps) {
+ // Long/thin so lets just find 1.
+ return 1;
+ }
+
+ }.testRelateWithRectangle();
+ }
+
+ private Point geoPointToSpatial4jPoint(GeoPoint geoPoint) {
+ return ctx.makePoint(geoPoint.getLongitude() * DistanceUtils.RADIANS_TO_DEGREES,
+ geoPoint.getLongitude() * DistanceUtils.RADIANS_TO_DEGREES);
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java
new file mode 100644
index 0000000..aac0a0a
--- /dev/null
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeSphereModelRectRelationTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.spatial.spatial4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.spatial4j.core.shape.Rectangle;
+import org.apache.lucene.geo3d.GeoArea;
+import org.apache.lucene.geo3d.GeoBBox;
+import org.apache.lucene.geo3d.GeoBBoxFactory;
+import org.apache.lucene.geo3d.GeoStandardCircle;
+import org.apache.lucene.geo3d.GeoPoint;
+import org.apache.lucene.geo3d.GeoPolygonFactory;
+import org.apache.lucene.geo3d.GeoShape;
+import org.apache.lucene.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<GeoPoint> 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 GeoStandardCircle(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());
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java
new file mode 100644
index 0000000..3b026c3
--- /dev/null
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.spatial.spatial4j;
+
+import org.apache.lucene.geo3d.GeoArea;
+import org.apache.lucene.geo3d.GeoBBox;
+import org.apache.lucene.geo3d.GeoBBoxFactory;
+import org.apache.lucene.geo3d.GeoCircle;
+import org.apache.lucene.geo3d.GeoStandardCircle;
+import org.apache.lucene.geo3d.GeoPath;
+import org.apache.lucene.geo3d.GeoPoint;
+import org.apache.lucene.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 GeoStandardCircle(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...)
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/RandomizedShapeTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/RandomizedShapeTestCase.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/RandomizedShapeTestCase.java
new file mode 100644
index 0000000..40d1b24
--- /dev/null
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/RandomizedShapeTestCase.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.spatial.spatial4j;
+
+import com.spatial4j.core.context.SpatialContext;
+import com.spatial4j.core.distance.DistanceUtils;
+import com.spatial4j.core.shape.Circle;
+import com.spatial4j.core.shape.Point;
+import com.spatial4j.core.shape.Rectangle;
+import com.spatial4j.core.shape.Shape;
+import com.spatial4j.core.shape.SpatialRelation;
+import com.spatial4j.core.shape.impl.Range;
+
+import static com.spatial4j.core.shape.SpatialRelation.CONTAINS;
+import static com.spatial4j.core.shape.SpatialRelation.WITHIN;
+
+import org.apache.lucene.util.LuceneTestCase;
+
+import static com.carrotsearch.randomizedtesting.RandomizedTest.*;
+
+/**
+ * A base test class with utility methods to help test shapes.
+ * Extends from RandomizedTest.
+ */
+public abstract class RandomizedShapeTestCase extends LuceneTestCase {
+
+ protected static final double EPS = 10e-9;
+
+ protected SpatialContext ctx;//needs to be set ASAP
+
+ /** Used to reduce the space of numbers to increase the likelihood that
+ * random numbers become equivalent, and thus trigger different code paths.
+ * Also makes some random shapes easier to manually examine.
+ */
+ protected final double DIVISIBLE = 2;// even coordinates; (not always used)
+
+ protected RandomizedShapeTestCase() {
+ }
+
+ public RandomizedShapeTestCase(SpatialContext ctx) {
+ this.ctx = ctx;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void checkShapesImplementEquals( Class<?>[] classes ) {
+ for( Class<?> clazz : classes ) {
+ try {
+ clazz.getDeclaredMethod( "equals", Object.class );
+ } catch (Exception e) {
+ fail("Shape needs to define 'equals' : " + clazz.getName());
+ }
+ try {
+ clazz.getDeclaredMethod( "hashCode" );
+ } catch (Exception e) {
+ fail("Shape needs to define 'hashCode' : " + clazz.getName());
+ }
+ }
+ }
+
+ //These few norm methods normalize the arguments for creating a shape to
+ // account for the dateline. Some tests loop past the dateline or have offsets
+ // that go past it and it's easier to have them coded that way and correct for
+ // it here. These norm methods should be used when needed, not frivolously.
+
+ protected double normX(double x) {
+ return ctx.isGeo() ? DistanceUtils.normLonDEG(x) : x;
+ }
+
+ protected double normY(double y) {
+ return ctx.isGeo() ? DistanceUtils.normLatDEG(y) : y;
+ }
+
+ protected Rectangle makeNormRect(double minX, double maxX, double minY, double maxY) {
+ if (ctx.isGeo()) {
+ if (Math.abs(maxX - minX) >= 360) {
+ minX = -180;
+ maxX = 180;
+ } else {
+ minX = DistanceUtils.normLonDEG(minX);
+ maxX = DistanceUtils.normLonDEG(maxX);
+ }
+
+ } else {
+ if (maxX < minX) {
+ double t = minX;
+ minX = maxX;
+ maxX = t;
+ }
+ minX = boundX(minX, ctx.getWorldBounds());
+ maxX = boundX(maxX, ctx.getWorldBounds());
+ }
+ if (maxY < minY) {
+ double t = minY;
+ minY = maxY;
+ maxY = t;
+ }
+ minY = boundY(minY, ctx.getWorldBounds());
+ maxY = boundY(maxY, ctx.getWorldBounds());
+ return ctx.makeRectangle(minX, maxX, minY, maxY);
+ }
+
+ public static double divisible(double v, double divisible) {
+ return (int) (Math.round(v / divisible) * divisible);
+ }
+
+ protected double divisible(double v) {
+ return divisible(v, DIVISIBLE);
+ }
+
+ /** reset()'s p, and confines to world bounds. Might not be divisible if
+ * the world bound isn't divisible too.
+ */
+ protected Point divisible(Point p) {
+ Rectangle bounds = ctx.getWorldBounds();
+ double newX = boundX( divisible(p.getX()), bounds );
+ double newY = boundY( divisible(p.getY()), bounds );
+ p.reset(newX, newY);
+ return p;
+ }
+
+ static double boundX(double i, Rectangle bounds) {
+ return bound(i, bounds.getMinX(), bounds.getMaxX());
+ }
+
+ static double boundY(double i, Rectangle bounds) {
+ return bound(i, bounds.getMinY(), bounds.getMaxY());
+ }
+
+ static double bound(double i, double min, double max) {
+ if (i < min) return min;
+ if (i > max) return max;
+ return i;
+ }
+
+ protected void assertRelation(SpatialRelation expected, Shape a, Shape b) {
+ assertRelation(null, expected, a, b);
+ }
+
+ protected void assertRelation(String msg, SpatialRelation expected, Shape a, Shape b) {
+ _assertIntersect(msg, expected, a, b);
+ //check flipped a & b w/ transpose(), while we're at it
+ _assertIntersect(msg, expected.transpose(), b, a);
+ }
+
+ private void _assertIntersect(String msg, SpatialRelation expected, Shape a, Shape b) {
+ SpatialRelation sect = a.relate(b);
+ if (sect == expected)
+ return;
+ msg = ((msg == null) ? "" : msg+"\r") + a +" intersect "+b;
+ if (expected == WITHIN || expected == CONTAINS) {
+ if (a.getClass().equals(b.getClass())) // they are the same shape type
+ assertEquals(msg,a,b);
+ else {
+ //they are effectively points or lines that are the same location
+ assertTrue(msg,!a.hasArea());
+ assertTrue(msg,!b.hasArea());
+
+ Rectangle aBBox = a.getBoundingBox();
+ Rectangle bBBox = b.getBoundingBox();
+ if (aBBox.getHeight() == 0 && bBBox.getHeight() == 0
+ && (aBBox.getMaxY() == 90 && bBBox.getMaxY() == 90
+ || aBBox.getMinY() == -90 && bBBox.getMinY() == -90))
+ ;//== a point at the pole
+ else
+ assertEquals(msg, aBBox, bBBox);
+ }
+ } else {
+ assertEquals(msg,expected,sect);//always fails
+ }
+ }
+
+ protected void assertEqualsRatio(String msg, double expected, double actual) {
+ double delta = Math.abs(actual - expected);
+ double base = Math.min(actual, expected);
+ double deltaRatio = base==0 ? delta : Math.min(delta,delta / base);
+ assertEquals(msg,0,deltaRatio, EPS);
+ }
+
+ protected int randomIntBetweenDivisible(int start, int end) {
+ return randomIntBetweenDivisible(start, end, (int)DIVISIBLE);
+ }
+ /** Returns a random integer between [start, end]. Integers between must be divisible by the 3rd argument. */
+ protected int randomIntBetweenDivisible(int start, int end, int divisible) {
+ // DWS: I tested this
+ int divisStart = (int) Math.ceil( (start+1) / (double)divisible );
+ int divisEnd = (int) Math.floor( (end-1) / (double)divisible );
+ int divisRange = Math.max(0,divisEnd - divisStart + 1);
+ int r = randomInt(1 + divisRange);//remember that '0' is counted
+ if (r == 0)
+ return start;
+ if (r == 1)
+ return end;
+ return (r-2 + divisStart)*divisible;
+ }
+
+ protected Rectangle randomRectangle(Point nearP) {
+ Rectangle bounds = ctx.getWorldBounds();
+ if (nearP == null)
+ nearP = randomPointIn(bounds);
+
+ Range xRange = randomRange(rarely() ? 0 : nearP.getX(), Range.xRange(bounds, ctx));
+ Range yRange = randomRange(rarely() ? 0 : nearP.getY(), Range.yRange(bounds, ctx));
+
+ return makeNormRect(
+ divisible(xRange.getMin()),
+ divisible(xRange.getMax()),
+ divisible(yRange.getMin()),
+ divisible(yRange.getMax()) );
+ }
+
+ private Range randomRange(double near, Range bounds) {
+ double mid = near + randomGaussian() * bounds.getWidth() / 6;
+ double width = Math.abs(randomGaussian()) * bounds.getWidth() / 6;//1/3rd
+ return new Range(mid - width / 2, mid + width / 2);
+ }
+
+ private double randomGaussianZeroTo(double max) {
+ if (max == 0)
+ return max;
+ assert max > 0;
+ double r;
+ do {
+ r = Math.abs(randomGaussian()) * (max * 0.50);
+ } while (r > max);
+ return r;
+ }
+
+ protected Rectangle randomRectangle(int divisible) {
+ double rX = randomIntBetweenDivisible(-180, 180, divisible);
+ double rW = randomIntBetweenDivisible(0, 360, divisible);
+ double rY1 = randomIntBetweenDivisible(-90, 90, divisible);
+ double rY2 = randomIntBetweenDivisible(-90, 90, divisible);
+ double rYmin = Math.min(rY1,rY2);
+ double rYmax = Math.max(rY1,rY2);
+ if (rW > 0 && rX == 180)
+ rX = -180;
+ return makeNormRect(rX, rX + rW, rYmin, rYmax);
+ }
+
+ protected Point randomPoint() {
+ return randomPointIn(ctx.getWorldBounds());
+ }
+
+ protected Point randomPointIn(Circle c) {
+ double d = c.getRadius() * randomDouble();
+ double angleDEG = 360 * randomDouble();
+ Point p = ctx.getDistCalc().pointOnBearing(c.getCenter(), d, angleDEG, ctx, null);
+ assertEquals(CONTAINS,c.relate(p));
+ return p;
+ }
+
+ protected Point randomPointIn(Rectangle r) {
+ double x = r.getMinX() + randomDouble()*r.getWidth();
+ double y = r.getMinY() + randomDouble()*r.getHeight();
+ x = normX(x);
+ y = normY(y);
+ Point p = ctx.makePoint(x,y);
+ assertEquals(CONTAINS,r.relate(p));
+ return p;
+ }
+
+ protected Point randomPointInOrNull(Shape shape) {
+ if (!shape.hasArea())// or try the center?
+ throw new UnsupportedOperationException("Need area to define shape!");
+ Rectangle bbox = shape.getBoundingBox();
+ for (int i = 0; i < 1000; i++) {
+ Point p = randomPointIn(bbox);
+ if (shape.relate(p).intersects()) {
+ return p;
+ }
+ }
+ return null;//tried too many times and failed
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java
new file mode 100644
index 0000000..1d559da
--- /dev/null
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPointTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.spatial.spatial4j.geo3d;
+
+import org.apache.lucene.geo3d.GeoPoint;
+import org.apache.lucene.geo3d.PlanetModel;
+import org.apache.lucene.util.LuceneTestCase;
+import org.junit.Test;
+
+import com.spatial4j.core.distance.DistanceUtils;
+
+import static com.carrotsearch.randomizedtesting.RandomizedTest.randomFloat;
+
+/**
+ * Test basic GeoPoint functionality.
+ */
+public class GeoPointTest extends LuceneTestCase {
+
+ @Test
+ public void testConversion() {
+ testPointRoundTrip(PlanetModel.SPHERE, 90 * DistanceUtils.DEGREES_TO_RADIANS, 0, 1e-6);
+ testPointRoundTrip(PlanetModel.SPHERE, -90 * DistanceUtils.DEGREES_TO_RADIANS, 0, 1e-6);
+ testPointRoundTrip(PlanetModel.WGS84, 90 * DistanceUtils.DEGREES_TO_RADIANS, 0, 1e-6);
+ testPointRoundTrip(PlanetModel.WGS84, -90 * DistanceUtils.DEGREES_TO_RADIANS, 0, 1e-6);
+
+ 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);
+ // In order to force the reverse conversion, we have to construct a geopoint from just x,y,z
+ final GeoPoint p2 = new GeoPoint(p1.x, p1.y, p1.z);
+ // Now, construct the final point based on getLatitude() and getLongitude()
+ final GeoPoint p3 = new GeoPoint(planetModel, p2.getLatitude(), p2.getLongitude());
+ double dist = p1.arcDistance(p3);
+ assertEquals(0, dist, epsilon);
+ }
+
+ @Test
+ public void testSurfaceDistance() {
+ final int times = atLeast(100);
+ for (int i = 0; i < times; i++) {
+ final double p1Lat = (randomFloat() * 180.0 - 90.0) * DistanceUtils.DEGREES_TO_RADIANS;
+ final double p1Lon = (randomFloat() * 360.0 - 180.0) * DistanceUtils.DEGREES_TO_RADIANS;
+ final double p2Lat = (randomFloat() * 180.0 - 90.0) * DistanceUtils.DEGREES_TO_RADIANS;
+ final double p2Lon = (randomFloat() * 360.0 - 180.0) * DistanceUtils.DEGREES_TO_RADIANS;
+ final GeoPoint p1 = new GeoPoint(PlanetModel.SPHERE, p1Lat, p1Lon);
+ final GeoPoint p2 = new GeoPoint(PlanetModel.SPHERE, p2Lat, p2Lon);
+ final double arcDistance = p1.arcDistance(p2);
+ // Compute ellipsoid distance; it should agree for a sphere
+ final double surfaceDistance = PlanetModel.SPHERE.surfaceDistance(p1,p2);
+ assertEquals(arcDistance, surfaceDistance, 1e-6);
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testBadLatLon() {
+ new GeoPoint(PlanetModel.SPHERE, 50.0, 32.2);
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial-extras/src/test/org/apache/lucene/spatial/vector/TestPointVectorStrategy.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/vector/TestPointVectorStrategy.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/vector/TestPointVectorStrategy.java
new file mode 100644
index 0000000..d62a0a8
--- /dev/null
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/vector/TestPointVectorStrategy.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.spatial.vector;
+
+import com.spatial4j.core.context.SpatialContext;
+import com.spatial4j.core.shape.Circle;
+import com.spatial4j.core.shape.Point;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.spatial.SpatialMatchConcern;
+import org.apache.lucene.spatial.StrategyTestCase;
+import org.apache.lucene.spatial.query.SpatialArgs;
+import org.apache.lucene.spatial.query.SpatialOperation;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class TestPointVectorStrategy extends StrategyTestCase {
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ this.ctx = SpatialContext.GEO;
+ this.strategy = new PointVectorStrategy(ctx, getClass().getSimpleName());
+ }
+
+ @Test
+ public void testCircleShapeSupport() {
+ Circle circle = ctx.makeCircle(ctx.makePoint(0, 0), 10);
+ SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects, circle);
+ Query query = this.strategy.makeQuery(args);
+
+ assertNotNull(query);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testInvalidQueryShape() {
+ Point point = ctx.makePoint(0, 0);
+ SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects, point);
+ this.strategy.makeQuery(args);
+ }
+
+ @Test
+ public void testCitiesIntersectsBBox() throws IOException {
+ getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS);
+ executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_Intersects_BBox);
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial/build.xml
----------------------------------------------------------------------
diff --git a/lucene/spatial/build.xml b/lucene/spatial/build.xml
index 4c348ee..08ab178 100644
--- a/lucene/spatial/build.xml
+++ b/lucene/spatial/build.xml
@@ -24,34 +24,4 @@
<import file="../module-build.xml"/>
- <path id="spatialjar">
- <fileset dir="lib"/>
- </path>
-
- <path id="classpath">
- <path refid="base.classpath"/>
- <path refid="spatialjar"/>
- <pathelement path="${queries.jar}" />
- <pathelement path="${misc.jar}" />
- <pathelement path="${spatial3d.jar}" />
- </path>
-
- <path id="test.classpath">
- <path refid="test.base.classpath" />
- <path refid="spatialjar"/>
- <pathelement path="src/test-files" />
- </path>
-
- <target name="compile-core" depends="jar-queries,jar-misc,jar-spatial3d,common.compile-core" />
-
- <target name="javadocs" depends="javadocs-queries,javadocs-misc,javadocs-spatial3d,compile-core,check-javadocs-uptodate"
- unless="javadocs-uptodate-${name}">
- <invoke-module-javadoc>
- <links>
- <link href="../queries"/>
- <link href="../misc"/>
- <link href="../spatial3d"/>
- </links>
- </invoke-module-javadoc>
- </target>
</project>
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial/ivy.xml
----------------------------------------------------------------------
diff --git a/lucene/spatial/ivy.xml b/lucene/spatial/ivy.xml
index 1625bfd..a427271 100644
--- a/lucene/spatial/ivy.xml
+++ b/lucene/spatial/ivy.xml
@@ -18,19 +18,4 @@
-->
<ivy-module version="2.0" xmlns:maven="http://ant.apache.org/ivy/maven">
<info organisation="org.apache.lucene" module="spatial"/>
- <configurations defaultconfmapping="compile->master;test->master">
- <conf name="compile" transitive="false"/>
- <conf name="test" transitive="false"/>
- </configurations>
- <dependencies>
- <dependency org="com.spatial4j" name="spatial4j" rev="${/com.spatial4j/spatial4j}" conf="compile"/>
-
- <dependency org="com.spatial4j" name="spatial4j" rev="${/com.spatial4j/spatial4j}" conf="test">
- <artifact name="spatial4j" type="test" ext="jar" maven:classifier="tests" />
- </dependency>
-
- <dependency org="org.slf4j" name="slf4j-api" rev="${/org.slf4j/slf4j-api}" conf="test"/>
-
- <exclude org="*" ext="*" matcher="regexp" type="${ivy.exclude.types}"/>
- </dependencies>
</ivy-module>
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial/src/java/org/apache/lucene/spatial/SpatialStrategy.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/SpatialStrategy.java b/lucene/spatial/src/java/org/apache/lucene/spatial/SpatialStrategy.java
deleted file mode 100644
index f433c11..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/SpatialStrategy.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.spatial;
-
-import com.spatial4j.core.context.SpatialContext;
-import com.spatial4j.core.shape.Point;
-import com.spatial4j.core.shape.Rectangle;
-import com.spatial4j.core.shape.Shape;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.valuesource.ReciprocalFloatFunction;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.spatial.query.SpatialArgs;
-
-/**
- * The SpatialStrategy encapsulates an approach to indexing and searching based
- * on shapes.
- * <p>
- * Different implementations will support different features. A strategy should
- * document these common elements:
- * <ul>
- * <li>Can it index more than one shape per field?</li>
- * <li>What types of shapes can be indexed?</li>
- * <li>What types of query shapes can be used?</li>
- * <li>What types of query operations are supported?
- * This might vary per shape.</li>
- * <li>Does it use some type of cache? When?
- * </ul>
- * If a strategy only supports certain shapes at index or query time, then in
- * general it will throw an exception if given an incompatible one. It will not
- * be coerced into compatibility.
- * <p>
- * Note that a SpatialStrategy is not involved with the Lucene stored field
- * values of shapes, which is immaterial to indexing and search.
- * <p>
- * Thread-safe.
- * <p>
- * This API is marked as experimental, however it is quite stable.
- *
- * @lucene.experimental
- */
-public abstract class SpatialStrategy {
-
- protected final SpatialContext ctx;
- private final String fieldName;
-
- /**
- * Constructs the spatial strategy with its mandatory arguments.
- */
- public SpatialStrategy(SpatialContext ctx, String fieldName) {
- if (ctx == null)
- throw new IllegalArgumentException("ctx is required");
- this.ctx = ctx;
- if (fieldName == null || fieldName.length() == 0)
- throw new IllegalArgumentException("fieldName is required");
- this.fieldName = fieldName;
- }
-
- public SpatialContext getSpatialContext() {
- return ctx;
- }
-
- /**
- * The name of the field or the prefix of them if there are multiple
- * fields needed internally.
- * @return Not null.
- */
- public String getFieldName() {
- return fieldName;
- }
-
- /**
- * Returns the IndexableField(s) from the {@code shape} that are to be
- * added to the {@link org.apache.lucene.document.Document}. These fields
- * are expected to be marked as indexed and not stored.
- * <p>
- * Note: If you want to <i>store</i> the shape as a string for retrieval in
- * search results, you could add it like this:
- * <pre>document.add(new StoredField(fieldName,ctx.toString(shape)));</pre>
- * The particular string representation used doesn't matter to the Strategy
- * since it doesn't use it.
- *
- * @return Not null nor will it have null elements.
- * @throws UnsupportedOperationException if given a shape incompatible with the strategy
- */
- public abstract Field[] createIndexableFields(Shape shape);
-
- /**
- * See {@link #makeDistanceValueSource(com.spatial4j.core.shape.Point, double)} called with
- * a multiplier of 1.0 (i.e. units of degrees).
- */
- public ValueSource makeDistanceValueSource(Point queryPoint) {
- return makeDistanceValueSource(queryPoint, 1.0);
- }
-
- /**
- * Make a ValueSource returning the distance between the center of the
- * indexed shape and {@code queryPoint}. If there are multiple indexed shapes
- * then the closest one is chosen. The result is multiplied by {@code multiplier}, which
- * conveniently is used to get the desired units.
- */
- public abstract ValueSource makeDistanceValueSource(Point queryPoint, double multiplier);
-
- /**
- * Make a Query based principally on {@link org.apache.lucene.spatial.query.SpatialOperation}
- * and {@link Shape} from the supplied {@code args}. It should be constant scoring of 1.
- *
- * @throws UnsupportedOperationException If the strategy does not support the shape in {@code args}
- * @throws org.apache.lucene.spatial.query.UnsupportedSpatialOperation If the strategy does not support the {@link
- * org.apache.lucene.spatial.query.SpatialOperation} in {@code args}.
- */
- public abstract Query makeQuery(SpatialArgs args);
-
- /**
- * Returns a ValueSource with values ranging from 1 to 0, depending inversely
- * on the distance from {@link #makeDistanceValueSource(com.spatial4j.core.shape.Point,double)}.
- * The formula is {@code c/(d + c)} where 'd' is the distance and 'c' is
- * one tenth the distance to the farthest edge from the center. Thus the
- * scores will be 1 for indexed points at the center of the query shape and as
- * low as ~0.1 at its furthest edges.
- */
- public final ValueSource makeRecipDistanceValueSource(Shape queryShape) {
- Rectangle bbox = queryShape.getBoundingBox();
- double diagonalDist = ctx.getDistCalc().distance(
- ctx.makePoint(bbox.getMinX(), bbox.getMinY()), bbox.getMaxX(), bbox.getMaxY());
- double distToEdge = diagonalDist * 0.5;
- float c = (float)distToEdge * 0.1f;//one tenth
- return new ReciprocalFloatFunction(makeDistanceValueSource(queryShape.getCenter(), 1.0), 1f, c, c);
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName()+" field:"+fieldName+" ctx="+ctx;
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxOverlapRatioValueSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxOverlapRatioValueSource.java b/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxOverlapRatioValueSource.java
deleted file mode 100644
index 9d0afe1..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxOverlapRatioValueSource.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.spatial.bbox;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.search.Explanation;
-
-import com.spatial4j.core.shape.Rectangle;
-
-/**
- * The algorithm is implemented as envelope on envelope (rect on rect) overlays rather than
- * complex polygon on complex polygon overlays.
- * <p>
- * Spatial relevance scoring algorithm:
- * <DL>
- * <DT>queryArea</DT> <DD>the area of the input query envelope</DD>
- * <DT>targetArea</DT> <DD>the area of the target envelope (per Lucene document)</DD>
- * <DT>intersectionArea</DT> <DD>the area of the intersection between the query and target envelopes</DD>
- * <DT>queryTargetProportion</DT> <DD>A 0-1 factor that divides the score proportion between query and target.
- * 0.5 is evenly.</DD>
- *
- * <DT>queryRatio</DT> <DD>intersectionArea / queryArea; (see note)</DD>
- * <DT>targetRatio</DT> <DD>intersectionArea / targetArea; (see note)</DD>
- * <DT>queryFactor</DT> <DD>queryRatio * queryTargetProportion;</DD>
- * <DT>targetFactor</DT> <DD>targetRatio * (1 - queryTargetProportion);</DD>
- * <DT>score</DT> <DD>queryFactor + targetFactor;</DD>
- * </DL>
- * Additionally, note that an optional minimum side length {@code minSideLength} may be used whenever an
- * area is calculated (queryArea, targetArea, intersectionArea). This allows for points or horizontal/vertical lines
- * to be used as the query shape and in such case the descending order should have smallest boxes up front. Without
- * this, a point or line query shape typically scores everything with the same value since there is 0 area.
- * <p>
- * Note: The actual computation of queryRatio and targetRatio is more complicated so that it considers
- * points and lines. Lines have the ratio of overlap, and points are either 1.0 or 0.0 depending on whether
- * it intersects or not.
- * <p>
- * Originally based on Geoportal's
- * <a href="http://geoportal.svn.sourceforge.net/svnroot/geoportal/Geoportal/trunk/src/com/esri/gpt/catalog/lucene/SpatialRankingValueSource.java">
- * SpatialRankingValueSource</a> but modified quite a bit. GeoPortal's algorithm will yield a score of 0
- * if either a line or point is compared, and it doesn't output a 0-1 normalized score (it multiplies the factors),
- * and it doesn't support minSideLength, and it had dateline bugs.
- *
- * @lucene.experimental
- */
-public class BBoxOverlapRatioValueSource extends BBoxSimilarityValueSource {
-
- private final boolean isGeo;//-180/+180 degrees (not part of identity; attached to parent strategy/field)
-
- private final Rectangle queryExtent;
- private final double queryArea;//not part of identity
-
- private final double minSideLength;
-
- private final double queryTargetProportion;
-
- //TODO option to compute geodetic area
-
- /**
- *
- * @param rectValueSource mandatory; source of rectangles
- * @param isGeo True if ctx.isGeo() and thus dateline issues should be attended to
- * @param queryExtent mandatory; the query rectangle
- * @param queryTargetProportion see class javadocs. Between 0 and 1.
- * @param minSideLength see class javadocs. 0.0 will effectively disable.
- */
- public BBoxOverlapRatioValueSource(ValueSource rectValueSource, boolean isGeo, Rectangle queryExtent,
- double queryTargetProportion, double minSideLength) {
- super(rectValueSource);
- this.isGeo = isGeo;
- this.minSideLength = minSideLength;
- this.queryExtent = queryExtent;
- this.queryArea = calcArea(queryExtent.getWidth(), queryExtent.getHeight());
- assert queryArea >= 0;
- this.queryTargetProportion = queryTargetProportion;
- if (queryTargetProportion < 0 || queryTargetProportion > 1.0)
- throw new IllegalArgumentException("queryTargetProportion must be >= 0 and <= 1");
- }
-
- /** Construct with 75% weighting towards target (roughly GeoPortal's default), geo degrees assumed, no
- * minimum side length. */
- public BBoxOverlapRatioValueSource(ValueSource rectValueSource, Rectangle queryExtent) {
- this(rectValueSource, true, queryExtent, 0.25, 0.0);
- }
-
- @Override
- public boolean equals(Object o) {
- if (!super.equals(o)) return false;
-
- BBoxOverlapRatioValueSource that = (BBoxOverlapRatioValueSource) o;
-
- if (Double.compare(that.minSideLength, minSideLength) != 0) return false;
- if (Double.compare(that.queryTargetProportion, queryTargetProportion) != 0) return false;
- if (!queryExtent.equals(that.queryExtent)) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = super.hashCode();
- long temp;
- result = 31 * result + queryExtent.hashCode();
- temp = Double.doubleToLongBits(minSideLength);
- result = 31 * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(queryTargetProportion);
- result = 31 * result + (int) (temp ^ (temp >>> 32));
- return result;
- }
-
- @Override
- protected String similarityDescription() {
- return queryExtent.toString() + "," + queryTargetProportion;
- }
-
- @Override
- protected double score(Rectangle target, AtomicReference<Explanation> exp) {
- // calculate "height": the intersection height between two boxes.
- double top = Math.min(queryExtent.getMaxY(), target.getMaxY());
- double bottom = Math.max(queryExtent.getMinY(), target.getMinY());
- double height = top - bottom;
- if (height < 0) {
- if (exp != null) {
- exp.set(Explanation.noMatch("No intersection"));
- }
- return 0;//no intersection
- }
-
- // calculate "width": the intersection width between two boxes.
- double width = 0;
- {
- Rectangle a = queryExtent;
- Rectangle b = target;
- if (a.getCrossesDateLine() == b.getCrossesDateLine()) {
- //both either cross or don't
- double left = Math.max(a.getMinX(), b.getMinX());
- double right = Math.min(a.getMaxX(), b.getMaxX());
- if (!a.getCrossesDateLine()) {//both don't
- if (left <= right) {
- width = right - left;
- } else if (isGeo && (Math.abs(a.getMinX()) == 180 || Math.abs(a.getMaxX()) == 180)
- && (Math.abs(b.getMinX()) == 180 || Math.abs(b.getMaxX()) == 180)) {
- width = 0;//both adjacent to dateline
- } else {
- if (exp != null) {
- exp.set(Explanation.noMatch("No intersection"));
- }
- return 0;//no intersection
- }
- } else {//both cross
- width = right - left + 360;
- }
- } else {
- if (!a.getCrossesDateLine()) {//then flip
- a = target;
- b = queryExtent;
- }
- //a crosses, b doesn't
- double qryWestLeft = Math.max(a.getMinX(), b.getMinX());
- double qryWestRight = b.getMaxX();
- if (qryWestLeft < qryWestRight)
- width += qryWestRight - qryWestLeft;
-
- double qryEastLeft = b.getMinX();
- double qryEastRight = Math.min(a.getMaxX(), b.getMaxX());
- if (qryEastLeft < qryEastRight)
- width += qryEastRight - qryEastLeft;
-
- if (qryWestLeft > qryWestRight && qryEastLeft > qryEastRight) {
- if (exp != null) {
- exp.set(Explanation.noMatch("No intersection"));
- }
- return 0;//no intersection
- }
- }
- }
-
- // calculate queryRatio and targetRatio
- double intersectionArea = calcArea(width, height);
- double queryRatio;
- if (queryArea > 0) {
- queryRatio = intersectionArea / queryArea;
- } else if (queryExtent.getHeight() > 0) {//vert line
- queryRatio = height / queryExtent.getHeight();
- } else if (queryExtent.getWidth() > 0) {//horiz line
- queryRatio = width / queryExtent.getWidth();
- } else {
- queryRatio = queryExtent.relate(target).intersects() ? 1 : 0;//could be optimized
- }
-
- double targetArea = calcArea(target.getWidth(), target.getHeight());
- assert targetArea >= 0;
- double targetRatio;
- if (targetArea > 0) {
- targetRatio = intersectionArea / targetArea;
- } else if (target.getHeight() > 0) {//vert line
- targetRatio = height / target.getHeight();
- } else if (target.getWidth() > 0) {//horiz line
- targetRatio = width / target.getWidth();
- } else {
- targetRatio = target.relate(queryExtent).intersects() ? 1 : 0;//could be optimized
- }
- assert queryRatio >= 0 && queryRatio <= 1 : queryRatio;
- assert targetRatio >= 0 && targetRatio <= 1 : targetRatio;
-
- // combine ratios into a score
-
- double queryFactor = queryRatio * queryTargetProportion;
- double targetFactor = targetRatio * (1.0 - queryTargetProportion);
- double score = queryFactor + targetFactor;
-
- if (exp!=null) {
- String minSideDesc = minSideLength > 0.0 ? " (minSide="+minSideLength+")" : "";
- exp.set(Explanation.match((float) score,
- this.getClass().getSimpleName()+": queryFactor + targetFactor",
- Explanation.match((float)intersectionArea, "IntersectionArea" + minSideDesc,
- Explanation.match((float)width, "width"),
- Explanation.match((float)height, "height"),
- Explanation.match((float)queryTargetProportion, "queryTargetProportion")),
- Explanation.match((float)queryFactor, "queryFactor",
- Explanation.match((float)targetRatio, "ratio"),
- Explanation.match((float)queryArea, "area of " + queryExtent + minSideDesc)),
- Explanation.match((float)targetFactor, "targetFactor",
- Explanation.match((float)targetRatio, "ratio"),
- Explanation.match((float)targetArea, "area of " + target + minSideDesc))));
- }
-
- return score;
- }
-
- /** Calculates the area while applying the minimum side length. */
- private double calcArea(double width, double height) {
- return Math.max(minSideLength, width) * Math.max(minSideLength, height);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/89db4950/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java b/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
deleted file mode 100644
index 15cd646..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.spatial.bbox;
-
-import java.io.IOException;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
-import org.apache.lucene.search.Explanation;
-import org.apache.lucene.search.IndexSearcher;
-
-import com.spatial4j.core.shape.Rectangle;
-
-/**
- * A base class for calculating a spatial relevance rank per document from a provided
- * {@link ValueSource} in which {@link FunctionValues#objectVal(int)} returns a {@link
- * com.spatial4j.core.shape.Rectangle}.
- * <p>
- * Implementers: remember to implement equals and hashCode if you have
- * fields!
- *
- * @lucene.experimental
- */
-public abstract class BBoxSimilarityValueSource extends ValueSource {
-
- private final ValueSource bboxValueSource;
-
- public BBoxSimilarityValueSource(ValueSource bboxValueSource) {
- this.bboxValueSource = bboxValueSource;
- }
-
- @Override
- public void createWeight(Map context, IndexSearcher searcher) throws IOException {
- bboxValueSource.createWeight(context, searcher);
- }
-
- @Override
- public String description() {
- return getClass().getSimpleName()+"(" + bboxValueSource.description() + "," + similarityDescription() + ")";
- }
-
- /** A comma-separated list of configurable items of the subclass to put into {@link #description()}. */
- protected abstract String similarityDescription();
-
- @Override
- public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-
- final FunctionValues shapeValues = bboxValueSource.getValues(context, readerContext);
-
- return new DoubleDocValues(this) {
- @Override
- public double doubleVal(int doc) {
- //? limit to Rect or call getBoundingBox()? latter would encourage bad practice
- final Rectangle rect = (Rectangle) shapeValues.objectVal(doc);
- return rect==null ? 0 : score(rect, null);
- }
-
- @Override
- public boolean exists(int doc) {
- return shapeValues.exists(doc);
- }
-
- @Override
- public Explanation explain(int doc) {
- final Rectangle rect = (Rectangle) shapeValues.objectVal(doc);
- if (rect == null)
- return Explanation.noMatch("no rect");
- AtomicReference<Explanation> explanation = new AtomicReference<>();
- score(rect, explanation);
- return explanation.get();
- }
- };
- }
-
- /**
- * Return a relevancy score. If {@code exp} is provided then diagnostic information is added.
- * @param rect The indexed rectangle; not null.
- * @param exp Optional diagnostic holder.
- * @return a score.
- */
- protected abstract double score(Rectangle rect, AtomicReference<Explanation> exp);
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;//same class
-
- BBoxSimilarityValueSource that = (BBoxSimilarityValueSource) o;
-
- if (!bboxValueSource.equals(that.bboxValueSource)) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- return bboxValueSource.hashCode();
- }
-}