diff --git a/dev-tools/maven/lucene/spatial-extras/pom.xml.template b/dev-tools/maven/lucene/spatial-extras/pom.xml.template
index c9f47145898..bae3556ff43 100644
--- a/dev-tools/maven/lucene/spatial-extras/pom.xml.template
+++ b/dev-tools/maven/lucene/spatial-extras/pom.xml.template
@@ -45,6 +45,13 @@
lucene-test-framework
test
+
+ org.apache.lucene
+ lucene-spatial3d
+ ${project.version}
+ test-jar
+ test
+
@lucene-spatial-extras.internal.dependencies@
@lucene-spatial-extras.external.dependencies@
@lucene-spatial-extras.internal.test.dependencies@
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 72c7fc3b118..fef3f139d75 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -28,6 +28,10 @@ New Features
* LUCENE-7392: Add point based LatLonBoundingBox as new RangeField Type.
(Nick Knize)
+* LUCENE-7951: Spatial-extras has much better Geo3d support by implementing Spatial4j
+ abstractions: SpatialContextFactory, ShapeFactory, BinaryCodec, DistanceCalculator.
+ (Ignacio Vera, David Smiley)
+
Optimizations
* LUCENE-7905: Optimize how OrdinalMap (used by
diff --git a/lucene/spatial-extras/build.xml b/lucene/spatial-extras/build.xml
index 4cfa4e53261..d95d935269b 100644
--- a/lucene/spatial-extras/build.xml
+++ b/lucene/spatial-extras/build.xml
@@ -38,10 +38,19 @@
+
+
+
+
+
+
+
+
+
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dBinaryCodec.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dBinaryCodec.java
new file mode 100644
index 00000000000..650e5610c9b
--- /dev/null
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dBinaryCodec.java
@@ -0,0 +1,130 @@
+/*
+ * 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.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.lucene.spatial3d.geom.GeoAreaShape;
+import org.apache.lucene.spatial3d.geom.GeoBBox;
+import org.apache.lucene.spatial3d.geom.GeoCircle;
+import org.apache.lucene.spatial3d.geom.GeoPointShape;
+import org.apache.lucene.spatial3d.geom.PlanetModel;
+import org.apache.lucene.spatial3d.geom.SerializableObject;
+import org.locationtech.spatial4j.context.SpatialContext;
+import org.locationtech.spatial4j.context.SpatialContextFactory;
+import org.locationtech.spatial4j.io.BinaryCodec;
+import org.locationtech.spatial4j.shape.Circle;
+import org.locationtech.spatial4j.shape.Point;
+import org.locationtech.spatial4j.shape.Rectangle;
+import org.locationtech.spatial4j.shape.Shape;
+import org.locationtech.spatial4j.shape.ShapeCollection;
+
+/**
+ * Geo3d implementation of {@link BinaryCodec}
+ *
+ * @lucene.experimental
+ */
+public class Geo3dBinaryCodec extends BinaryCodec {
+
+ private PlanetModel planetModel;
+
+ @SuppressWarnings("unchecked")
+ public Geo3dBinaryCodec(SpatialContext ctx, SpatialContextFactory factory) {
+ super(ctx, factory);
+ planetModel = ((Geo3dSpatialContextFactory) factory).planetModel;
+ }
+
+ @Override
+ public Shape readShape(DataInput dataInput) throws IOException {
+ SerializableObject serializableObject = SerializableObject.readObject(planetModel, (InputStream) dataInput);
+ if (serializableObject instanceof GeoAreaShape) {
+ GeoAreaShape shape = (GeoAreaShape) serializableObject;
+ return new Geo3dShape<>(shape, ctx);
+ }
+ throw new IllegalArgumentException("trying to read a not supported shape: " + serializableObject.getClass());
+ }
+
+ @Override
+ public void writeShape(DataOutput dataOutput, Shape s) throws IOException {
+ if (s instanceof Geo3dShape) {
+ Geo3dShape geoAreaShape = (Geo3dShape) s;
+ SerializableObject.writeObject((OutputStream) dataOutput, geoAreaShape.shape);
+ } else {
+ throw new IllegalArgumentException("trying to write a not supported shape: " + s.getClass().getName());
+ }
+ }
+
+ @Override
+ public Point readPoint(DataInput dataInput) throws IOException {
+ SerializableObject serializableObject = SerializableObject.readObject(planetModel, (InputStream) dataInput);
+ if (serializableObject instanceof GeoPointShape) {
+ GeoPointShape shape = (GeoPointShape) serializableObject;
+ return new Geo3dPointShape(shape, ctx);
+ }
+ throw new IllegalArgumentException("trying to read a not supported point shape: " + serializableObject.getClass());
+ }
+
+ @Override
+ public void writePoint(DataOutput dataOutput, Point pt) throws IOException {
+ writeShape(dataOutput, pt);
+ }
+
+ @Override
+ public Rectangle readRect(DataInput dataInput) throws IOException {
+ SerializableObject serializableObject = SerializableObject.readObject(planetModel, (InputStream) dataInput);
+ if (serializableObject instanceof GeoBBox) {
+ GeoBBox shape = (GeoBBox) serializableObject;
+ return new Geo3dRectangleShape(shape, ctx);
+ }
+ throw new IllegalArgumentException("trying to read a not supported rectangle shape: " + serializableObject.getClass());
+ }
+
+ @Override
+ public void writeRect(DataOutput dataOutput, Rectangle r) throws IOException {
+ writeShape(dataOutput, r);
+ }
+
+ @Override
+ public Circle readCircle(DataInput dataInput) throws IOException {
+ SerializableObject serializableObject = SerializableObject.readObject(planetModel, (InputStream) dataInput);
+ if (serializableObject instanceof GeoCircle) {
+ GeoCircle shape = (GeoCircle) serializableObject;
+ return new Geo3dCircleShape(shape, ctx);
+ }
+ throw new IllegalArgumentException("trying to read a not supported circle shape: " + serializableObject.getClass());
+ }
+
+ @Override
+ public void writeCircle(DataOutput dataOutput, Circle c) throws IOException {
+ writeShape(dataOutput, c);
+ }
+
+ @Override
+ public ShapeCollection readCollection(DataInput dataInput) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void writeCollection(DataOutput dataOutput, ShapeCollection col) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dCircleShape.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dCircleShape.java
new file mode 100644
index 00000000000..ccef92a4bc9
--- /dev/null
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dCircleShape.java
@@ -0,0 +1,82 @@
+/*
+ * 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.spatial3d.geom.GeoCircle;
+import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
+import org.apache.lucene.spatial3d.geom.GeoPointShapeFactory;
+import org.apache.lucene.spatial3d.geom.PlanetModel;
+import org.locationtech.spatial4j.context.SpatialContext;
+import org.locationtech.spatial4j.distance.DistanceUtils;
+import org.locationtech.spatial4j.shape.Circle;
+import org.locationtech.spatial4j.shape.Point;
+import org.locationtech.spatial4j.shape.Shape;
+import org.locationtech.spatial4j.shape.SpatialRelation;
+
+/**
+ * Specialization of a {@link Geo3dShape} which represents a {@link Circle}.
+ *
+ * @lucene.experimental
+ */
+public class Geo3dCircleShape extends Geo3dShape implements Circle {
+
+ public Geo3dCircleShape(final GeoCircle shape, final SpatialContext spatialcontext) {
+ super(shape, spatialcontext);
+ }
+
+ @Override
+ public void reset(double x, double y, double radiusDEG) {
+ shape = GeoCircleFactory.makeGeoCircle(shape.getPlanetModel(),
+ y * DistanceUtils.DEGREES_TO_RADIANS,
+ x * DistanceUtils.DEGREES_TO_RADIANS,
+ radiusDEG * DistanceUtils.DEGREES_TO_RADIANS);
+ center = null;
+ boundingBox = null;
+ }
+
+ @Override
+ public double getRadius() {
+ return shape.getRadius() * DistanceUtils.RADIANS_TO_DEGREES;
+ }
+
+ @Override
+ public Point getCenter() {
+ Point center = this.center;//volatile read once
+ if (center == null) {
+ center = new Geo3dPointShape(
+ GeoPointShapeFactory.makeGeoPointShape(shape.getPlanetModel(),
+ shape.getCenter().getLatitude(),
+ shape.getCenter().getLongitude()),
+ spatialcontext);
+ this.center = center;
+ }
+ return center;
+ }
+
+ //TODO Improve GeoCircle to properly relate a point with WGS84 model -- LUCENE-7970
+ @Override
+ public SpatialRelation relate(Shape other) {
+ if (shape.getPlanetModel() != PlanetModel.SPHERE && other instanceof Point) {
+ if (spatialcontext.getDistCalc().distance((Point) other, getCenter()) <= getRadius()) {
+ return SpatialRelation.CONTAINS;
+ }
+ return SpatialRelation.DISJOINT;
+ }
+ return super.relate(other);
+ }
+}
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dDistanceCalculator.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dDistanceCalculator.java
new file mode 100644
index 00000000000..5154de431d4
--- /dev/null
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dDistanceCalculator.java
@@ -0,0 +1,154 @@
+/*
+ * 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.spatial3d.geom.GeoPoint;
+import org.apache.lucene.spatial3d.geom.GeoPointShape;
+import org.apache.lucene.spatial3d.geom.PlanetModel;
+import org.locationtech.spatial4j.context.SpatialContext;
+import org.locationtech.spatial4j.distance.DistanceCalculator;
+import org.locationtech.spatial4j.distance.DistanceUtils;
+import org.locationtech.spatial4j.shape.Circle;
+import org.locationtech.spatial4j.shape.Point;
+import org.locationtech.spatial4j.shape.Rectangle;
+
+/**
+ * Geo3d implementation of {@link DistanceCalculator}
+ *
+ * @lucene.experimental
+ */
+public class Geo3dDistanceCalculator implements DistanceCalculator {
+
+ protected final PlanetModel planetModel;
+
+ public Geo3dDistanceCalculator(PlanetModel planetModel) {
+ this.planetModel = planetModel;
+ }
+
+ @Override
+ public double distance(Point from, Point to) {
+ if (from instanceof Geo3dPointShape && to instanceof Geo3dPointShape) {
+ GeoPointShape pointShape1 = ((Geo3dPointShape) from).shape;
+ GeoPointShape pointShape2 = ((Geo3dPointShape) to).shape;
+ return planetModel.surfaceDistance(pointShape1.getCenter(), pointShape2.getCenter()) * DistanceUtils.RADIANS_TO_DEGREES;
+ }
+ return distance(from, to.getX(), to.getY());
+ }
+
+ @Override
+ public double distance(Point from, double toX, double toY) {
+ GeoPoint fromGeoPoint;
+ if (from instanceof Geo3dPointShape) {
+ fromGeoPoint = (((Geo3dPointShape) from).shape).getCenter();
+ } else {
+ fromGeoPoint = new GeoPoint(planetModel,
+ from.getY() * DistanceUtils.DEGREES_TO_RADIANS,
+ from.getX() * DistanceUtils.DEGREES_TO_RADIANS);
+ }
+ GeoPoint toGeoPoint = new GeoPoint(planetModel,
+ toY * DistanceUtils.DEGREES_TO_RADIANS,
+ toX * DistanceUtils.DEGREES_TO_RADIANS);
+ return planetModel.surfaceDistance(fromGeoPoint, toGeoPoint) * DistanceUtils.RADIANS_TO_DEGREES;
+ }
+
+ @Override
+ public boolean within(Point from, double toX, double toY, double distance) {
+ return (distance < distance(from, toX, toY));
+ }
+
+ @Override
+ public Point pointOnBearing(Point from, double distDEG, double bearingDEG, SpatialContext ctx, Point reuse) {
+ // Algorithm using Vincenty's formulae (https://en.wikipedia.org/wiki/Vincenty%27s_formulae)
+ // which takes into account that planets may not be spherical.
+ //Code adaptation from http://www.movable-type.co.uk/scripts/latlong-vincenty.html
+ Geo3dPointShape geoFrom = (Geo3dPointShape) from;
+ GeoPoint point = (GeoPoint) geoFrom.shape;
+ double lat = point.getLatitude();
+ double lon = point.getLongitude();
+ double dist = DistanceUtils.DEGREES_TO_RADIANS * distDEG;
+ double bearing = DistanceUtils.DEGREES_TO_RADIANS * bearingDEG;
+
+ double sinα1 = Math.sin(bearing);
+ double cosα1 = Math.cos(bearing);
+
+ double tanU1 = (1 - planetModel.flattening) * Math.tan(lat);
+ double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1));
+ double sinU1 = tanU1 * cosU1;
+
+ double σ1 = Math.atan2(tanU1, cosα1);
+ double sinα = cosU1 * sinα1;
+ double cosSqα = 1 - sinα * sinα;
+ double uSq = cosSqα * planetModel.squareRatio;// (planetModel.ab* planetModel.ab - planetModel.c*planetModel.c) / (planetModel.c*planetModel.c);
+ double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
+ double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
+
+ double cos2σM;
+ double sinσ;
+ double cosσ;
+ double Δσ;
+
+ double σ = dist / (planetModel.c * A);
+ double σʹ;
+ double iterations = 0;
+ do {
+ cos2σM = Math.cos(2 * σ1 + σ);
+ sinσ = Math.sin(σ);
+ cosσ = Math.cos(σ);
+ Δσ = B * sinσ * (cos2σM + B / 4 * (cosσ * (-1 + 2 * cos2σM * cos2σM) -
+ B / 6 * cos2σM * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σM * cos2σM)));
+ σʹ = σ;
+ σ = dist / (planetModel.c * A) + Δσ;
+ } while (Math.abs(σ - σʹ) > 1e-12 && ++iterations < 200);
+
+ if (iterations >= 200) {
+ throw new RuntimeException("Formula failed to converge");
+ }
+
+ double x = sinU1 * sinσ - cosU1 * cosσ * cosα1;
+ double φ2 = Math.atan2(sinU1 * cosσ + cosU1 * sinσ * cosα1, (1 - planetModel.flattening) * Math.sqrt(sinα * sinα + x * x));
+ double λ = Math.atan2(sinσ * sinα1, cosU1 * cosσ - sinU1 * sinσ * cosα1);
+ double C = planetModel.flattening / 16 * cosSqα * (4 + planetModel.flattening * (4 - 3 * cosSqα));
+ double L = λ - (1 - C) * planetModel.flattening * sinα *
+ (σ + C * sinσ * (cos2σM + C * cosσ * (-1 + 2 * cos2σM * cos2σM)));
+ double λ2 = (lon + L + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise to -180..+180
+
+ return ctx.getShapeFactory().pointXY(λ2 * DistanceUtils.RADIANS_TO_DEGREES,
+ φ2 * DistanceUtils.RADIANS_TO_DEGREES);
+ }
+
+ @Override
+ public Rectangle calcBoxByDistFromPt(Point from, double distDEG, SpatialContext ctx, Rectangle reuse) {
+ Circle circle = ctx.getShapeFactory().circle(from, distDEG);
+ return circle.getBoundingBox();
+ }
+
+ @Override
+ public double calcBoxByDistFromPt_yHorizAxisDEG(Point from, double distDEG, SpatialContext ctx) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public double area(Rectangle rect) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public double area(Circle circle) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dPointShape.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dPointShape.java
new file mode 100644
index 00000000000..c0d127df914
--- /dev/null
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dPointShape.java
@@ -0,0 +1,78 @@
+/*
+ * 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.spatial3d.geom.GeoPointShape;
+import org.apache.lucene.spatial3d.geom.GeoPointShapeFactory;
+import org.locationtech.spatial4j.context.SpatialContext;
+import org.locationtech.spatial4j.distance.DistanceUtils;
+import org.locationtech.spatial4j.shape.Point;
+import org.locationtech.spatial4j.shape.Rectangle;
+import org.locationtech.spatial4j.shape.Shape;
+
+/**
+ * Specialization of a {@link Geo3dShape} which represents a {@link Point}.
+ *
+ * @lucene.experimental
+ */
+public class Geo3dPointShape extends Geo3dShape implements Point {
+
+ public Geo3dPointShape(final GeoPointShape shape, final SpatialContext spatialcontext) {
+ super(shape, spatialcontext);
+ center = this;
+ }
+
+ @Override
+ public void reset(double x, double y) {
+ shape = GeoPointShapeFactory.makeGeoPointShape(shape.getPlanetModel(),
+ y * DistanceUtils.DEGREES_TO_RADIANS,
+ x * DistanceUtils.DEGREES_TO_RADIANS);
+ center = this;
+ boundingBox = null;
+ }
+
+ @Override
+ public double getX() {
+ return shape.getCenter().getLongitude() * DistanceUtils.RADIANS_TO_DEGREES;
+ }
+
+ @Override
+ public double getY() {
+ return shape.getCenter().getLatitude() * DistanceUtils.RADIANS_TO_DEGREES;
+ }
+
+ @Override
+ public Rectangle getBoundingBox() {
+ Rectangle bbox = this.boundingBox;//volatile read once
+ if (bbox == null) {
+ bbox = new Geo3dRectangleShape(shape, spatialcontext);
+ this.boundingBox = bbox;
+ }
+ return bbox;
+ }
+
+ @Override
+ public Shape getBuffered(double distance, SpatialContext spatialContext) {
+ return spatialContext.getShapeFactory().circle(getX(), getY(), distance);
+ }
+
+ @Override
+ public boolean hasArea() {
+ return false;
+ }
+}
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dRectangleShape.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dRectangleShape.java
new file mode 100644
index 00000000000..d354dcc698a
--- /dev/null
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dRectangleShape.java
@@ -0,0 +1,163 @@
+/*
+ * 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.spatial3d.geom.GeoBBox;
+import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
+import org.apache.lucene.spatial3d.geom.GeoPoint;
+import org.apache.lucene.spatial3d.geom.GeoPointShapeFactory;
+import org.apache.lucene.spatial3d.geom.LatLonBounds;
+import org.locationtech.spatial4j.context.SpatialContext;
+import org.locationtech.spatial4j.distance.DistanceUtils;
+import org.locationtech.spatial4j.shape.Point;
+import org.locationtech.spatial4j.shape.Rectangle;
+import org.locationtech.spatial4j.shape.Shape;
+import org.locationtech.spatial4j.shape.SpatialRelation;
+
+/**
+ * Specialization of a {@link Geo3dShape} which represents a {@link Rectangle}.
+ *
+ * @lucene.experimental
+ */
+public class Geo3dRectangleShape extends Geo3dShape implements Rectangle {
+
+ private double minX;
+ private double maxX;
+ private double minY;
+ private double maxY;
+
+ public Geo3dRectangleShape(final GeoBBox shape,
+ final SpatialContext spatialcontext,
+ double minX,
+ double maxX,
+ double minY,
+ double maxY) {
+ super(shape, spatialcontext);
+ this.minX = minX;
+ this.maxX = maxX;
+ this.minY = minY;
+ this.maxY = maxY;
+ }
+
+ public Geo3dRectangleShape(final GeoBBox shape, final SpatialContext spatialcontext) {
+ super(shape, spatialcontext);
+ setBoundsFromshape();
+ }
+
+
+ /**
+ * Set the bounds from the wrapped GeoBBox.
+ */
+ private void setBoundsFromshape() {
+ LatLonBounds bounds = new LatLonBounds();
+ shape.getBounds(bounds);
+ minX = bounds.checkNoLongitudeBound() ? -180.0 : bounds.getLeftLongitude() * DistanceUtils.RADIANS_TO_DEGREES;
+ minY = bounds.checkNoBottomLatitudeBound() ? -90.0 : bounds.getMinLatitude() * DistanceUtils.RADIANS_TO_DEGREES;
+ maxX = bounds.checkNoLongitudeBound() ? 180.0 : bounds.getRightLongitude() * DistanceUtils.RADIANS_TO_DEGREES;
+ maxY = bounds.checkNoTopLatitudeBound() ? 90.0 : bounds.getMaxLatitude() * DistanceUtils.RADIANS_TO_DEGREES;
+ }
+
+ @Override
+ public Point getCenter() {
+ Point center = this.center;//volatile read once
+ if (center == null) {
+ GeoPoint point = shape.getCenter();
+ center = new Geo3dPointShape(
+ GeoPointShapeFactory.makeGeoPointShape(shape.getPlanetModel(),
+ point.getLatitude(),
+ point.getLongitude()),
+ spatialcontext);
+ this.center = center;
+ }
+ return center;
+ }
+
+ @Override
+ public void reset(double minX, double maxX, double minY, double maxY) {
+ shape = GeoBBoxFactory.makeGeoBBox(shape.getPlanetModel(),
+ maxY * DistanceUtils.DEGREES_TO_RADIANS,
+ minY * DistanceUtils.DEGREES_TO_RADIANS,
+ minX * DistanceUtils.DEGREES_TO_RADIANS,
+ maxX * DistanceUtils.DEGREES_TO_RADIANS);
+ center = null;
+ boundingBox = null;
+ }
+
+ @Override
+ public Rectangle getBoundingBox() {
+ return this;
+ }
+
+ @Override
+ public double getWidth() {
+ double result = getMaxX() - getMinX();
+ if (result < 0) {
+ result += 360;
+ }
+ return result;
+ }
+
+ @Override
+ public double getHeight() {
+ return getMaxY() - getMinY();
+ }
+
+ @Override
+ public double getMinX() {
+ return minX;
+ }
+
+ @Override
+ public double getMinY() {
+ return minY;
+ }
+
+ @Override
+ public double getMaxX() {
+ return maxX;
+ }
+
+ @Override
+ public double getMaxY() {
+ return maxY;
+ }
+
+ @Override
+ public boolean getCrossesDateLine() {
+ return (getMaxX() > 0 && getMinX() < 0);
+
+ }
+
+ @Override
+ public SpatialRelation relateYRange(double minY, double maxY) {
+ Rectangle r = spatialcontext.getShapeFactory().rect(-180, 180, minY, maxY);
+ return relate(r);
+ }
+
+ @Override
+ public SpatialRelation relateXRange(double minX, double maxX) {
+ Rectangle r = spatialcontext.getShapeFactory().rect(minX, maxX, -90, 90);
+ return relate(r);
+ }
+
+ @Override
+ public Shape getBuffered(double distance, SpatialContext spatialContext) {
+ GeoBBox bBox = shape.expand(distance * DistanceUtils.DEGREES_TO_RADIANS);
+ return new Geo3dRectangleShape(bBox, spatialContext);
+ }
+}
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java
index 9fa6d8e5d5f..bbe1f3a45d6 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShape.java
@@ -14,127 +14,110 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.lucene.spatial.spatial4j;
+import org.apache.lucene.spatial3d.geom.GeoArea;
+import org.apache.lucene.spatial3d.geom.GeoAreaFactory;
+import org.apache.lucene.spatial3d.geom.GeoAreaShape;
+import org.apache.lucene.spatial3d.geom.GeoBBox;
+import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
+import org.apache.lucene.spatial3d.geom.GeoPoint;
+import org.apache.lucene.spatial3d.geom.LatLonBounds;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.distance.DistanceUtils;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Rectangle;
import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.SpatialRelation;
-import org.locationtech.spatial4j.shape.impl.RectangleImpl;
-import org.apache.lucene.spatial3d.geom.LatLonBounds;
-import org.apache.lucene.spatial3d.geom.GeoArea;
-import org.apache.lucene.spatial3d.geom.GeoAreaFactory;
-import org.apache.lucene.spatial3d.geom.GeoPoint;
-import org.apache.lucene.spatial3d.geom.GeoShape;
-import org.apache.lucene.spatial3d.geom.PlanetModel;
/**
- * A Spatial4j Shape wrapping a {@link GeoShape} ("Geo3D") -- a 3D planar geometry based Spatial4j Shape implementation.
+ * A Spatial4j Shape wrapping a {@link GeoAreaShape} ("Geo3D") -- a 3D planar geometry
+ * based Spatial4j Shape implementation.
* Geo3D implements shapes on the surface of a sphere or ellipsoid.
*
+ * @param is the type of {@link GeoAreaShape}
* @lucene.experimental
*/
-public class Geo3dShape implements Shape {
- /** The required size of this adjustment depends on the actual planetary model chosen.
- * This value is big enough to account for WGS84. */
- protected static final double ROUNDOFF_ADJUSTMENT = 0.05;
- public final SpatialContext ctx;
- public final GeoShape shape;
- public final PlanetModel planetModel;
+public class Geo3dShape implements Shape {
- private volatile Rectangle boundingBox = null; // lazy initialized
+ protected final SpatialContext spatialcontext;
- public Geo3dShape(final GeoShape shape, final SpatialContext ctx) {
- this(PlanetModel.SPHERE, shape, ctx);
- }
+ protected T shape;
+ protected volatile Rectangle boundingBox = null; // lazy initialized
+ protected volatile Point center = null; // lazy initialized
- 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;
+ public Geo3dShape(final T shape, final SpatialContext spatialcontext) {
+ this.spatialcontext = spatialcontext;
this.shape = shape;
}
- @Override
- public SpatialContext getContext() {
- return ctx;
- }
-
@Override
public SpatialRelation relate(Shape other) {
- if (other instanceof Rectangle)
- return relate((Rectangle)other);
- else if (other instanceof Point)
- return relate((Point)other);
- else
+ int relationship;
+ if (other instanceof Geo3dShape>) {
+ relationship = relate((Geo3dShape>) other);
+ } else if (other instanceof Rectangle) {
+ relationship = relate((Rectangle) other);
+ } else if (other instanceof Point) {
+ relationship = relate((Point) other);
+ } else {
throw new RuntimeException("Unimplemented shape relationship determination: " + other.getClass());
+ }
+
+ switch (relationship) {
+ case GeoArea.DISJOINT:
+ return SpatialRelation.DISJOINT;
+ case GeoArea.OVERLAPS:
+ return (other instanceof Point ? SpatialRelation.CONTAINS : SpatialRelation.INTERSECTS);
+ case GeoArea.CONTAINS:
+ return (other instanceof Point ? SpatialRelation.CONTAINS : SpatialRelation.WITHIN);
+ case GeoArea.WITHIN:
+ return SpatialRelation.CONTAINS;
+ }
+
+ throw new RuntimeException("Undetermined shape relationship: " + relationship);
}
- protected SpatialRelation relate(Rectangle r) {
+ private int relate(Geo3dShape> s) {
+ return shape.getRelationship(s.shape);
+ }
+
+ private int relate(Rectangle r) {
// Construct the right kind of GeoArea first
- GeoArea geoArea = GeoAreaFactory.makeGeoArea(planetModel,
+ GeoArea geoArea = GeoAreaFactory.makeGeoArea(shape.getPlanetModel(),
r.getMaxY() * DistanceUtils.DEGREES_TO_RADIANS,
r.getMinY() * DistanceUtils.DEGREES_TO_RADIANS,
r.getMinX() * DistanceUtils.DEGREES_TO_RADIANS,
r.getMaxX() * DistanceUtils.DEGREES_TO_RADIANS);
- int relationship = geoArea.getRelationship(shape);
- if (relationship == GeoArea.WITHIN)
- return SpatialRelation.WITHIN;
- else if (relationship == GeoArea.CONTAINS)
- return SpatialRelation.CONTAINS;
- else if (relationship == GeoArea.OVERLAPS)
- return SpatialRelation.INTERSECTS;
- else if (relationship == GeoArea.DISJOINT)
- return SpatialRelation.DISJOINT;
- else
- throw new RuntimeException("Unknown relationship returned: "+relationship);
+
+ return geoArea.getRelationship(shape);
}
- protected SpatialRelation relate(Point p) {
- // Create a GeoPoint
- GeoPoint point = new GeoPoint(planetModel, p.getY()* DistanceUtils.DEGREES_TO_RADIANS, p.getX()* DistanceUtils.DEGREES_TO_RADIANS);
+ private int relate(Point p) {
+ GeoPoint point = new GeoPoint(shape.getPlanetModel(),
+ p.getY() * DistanceUtils.DEGREES_TO_RADIANS,
+ p.getX() * DistanceUtils.DEGREES_TO_RADIANS);
+
if (shape.isWithin(point)) {
- // Point within shape
- return SpatialRelation.CONTAINS;
+ return GeoArea.WITHIN;
}
- return SpatialRelation.DISJOINT;
+ return GeoArea.DISJOINT;
}
-
-
@Override
public Rectangle getBoundingBox() {
Rectangle bbox = this.boundingBox;//volatile read once
if (bbox == null) {
LatLonBounds bounds = new LatLonBounds();
shape.getBounds(bounds);
- double leftLon;
- double rightLon;
- if (bounds.checkNoLongitudeBound()) {
- leftLon = -180.0;
- rightLon = 180.0;
- } else {
- leftLon = bounds.getLeftLongitude().doubleValue() * DistanceUtils.RADIANS_TO_DEGREES;
- rightLon = bounds.getRightLongitude().doubleValue() * DistanceUtils.RADIANS_TO_DEGREES;
- }
- double minLat;
- if (bounds.checkNoBottomLatitudeBound()) {
- minLat = -90.0;
- } else {
- minLat = bounds.getMinLatitude().doubleValue() * DistanceUtils.RADIANS_TO_DEGREES;
- }
- double maxLat;
- if (bounds.checkNoTopLatitudeBound()) {
- maxLat = 90.0;
- } else {
- maxLat = bounds.getMaxLatitude().doubleValue() * DistanceUtils.RADIANS_TO_DEGREES;
- }
- bbox = new RectangleImpl(leftLon, rightLon, minLat, maxLat, ctx).getBuffered(ROUNDOFF_ADJUSTMENT, ctx);
+ double leftLon = bounds.checkNoLongitudeBound() ? -Math.PI : bounds.getLeftLongitude();
+ double rightLon = bounds.checkNoLongitudeBound() ? Math.PI : bounds.getRightLongitude();
+ double minLat = bounds.checkNoBottomLatitudeBound() ? -Math.PI * 0.5 : bounds.getMinLatitude();
+ double maxLat = bounds.checkNoTopLatitudeBound() ? Math.PI * 0.5 : bounds.getMaxLatitude();
+ GeoBBox geoBBox = GeoBBoxFactory.makeGeoBBox(shape.getPlanetModel(), maxLat, minLat, leftLon, rightLon);
+ bbox = new Geo3dRectangleShape(geoBBox, spatialcontext);
this.boundingBox = bbox;
}
return bbox;
@@ -146,17 +129,22 @@ public class Geo3dShape implements Shape {
}
@Override
- public double getArea(SpatialContext ctx) {
+ public double getArea(SpatialContext spatialContext) {
throw new UnsupportedOperationException();
}
@Override
public Point getCenter() {
- throw new UnsupportedOperationException();
+ Point center = this.center;//volatile read once
+ if (center == null) {
+ center = getBoundingBox().getCenter();
+ this.center = center;
+ }
+ return center;
}
@Override
- public Shape getBuffered(double distance, SpatialContext ctx) {
+ public Shape getBuffered(double distance, SpatialContext spatialContext) {
throw new UnsupportedOperationException();
}
@@ -166,20 +154,20 @@ public class Geo3dShape implements Shape {
}
@Override
- public String toString() {
- return "Geo3dShape{planetmodel=" + planetModel + ", shape=" + shape + '}';
+ public SpatialContext getContext() {
+ return spatialcontext;
}
@Override
- public boolean equals(Object other) {
- if (!(other instanceof Geo3dShape))
+ public boolean equals(Object o) {
+ if (!(o instanceof Geo3dShape>))
return false;
- Geo3dShape tr = (Geo3dShape)other;
- return tr.ctx.equals(ctx) && tr.planetModel.equals(planetModel) && tr.shape.equals(shape);
+ final Geo3dShape> other = (Geo3dShape>) o;
+ return (other.spatialcontext.equals(spatialcontext) && other.shape.equals(shape));
}
@Override
public int hashCode() {
- return planetModel.hashCode() + shape.hashCode();
+ return spatialcontext.hashCode() + shape.hashCode();
}
}
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShapeFactory.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShapeFactory.java
new file mode 100644
index 00000000000..a80a0439b29
--- /dev/null
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dShapeFactory.java
@@ -0,0 +1,396 @@
+/*
+ * 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 org.apache.lucene.spatial3d.geom.GeoBBox;
+import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
+import org.apache.lucene.spatial3d.geom.GeoCircle;
+import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
+import org.apache.lucene.spatial3d.geom.GeoCompositeAreaShape;
+import org.apache.lucene.spatial3d.geom.GeoPath;
+import org.apache.lucene.spatial3d.geom.GeoPathFactory;
+import org.apache.lucene.spatial3d.geom.GeoPoint;
+import org.apache.lucene.spatial3d.geom.GeoPointShape;
+import org.apache.lucene.spatial3d.geom.GeoPointShapeFactory;
+import org.apache.lucene.spatial3d.geom.GeoPolygon;
+import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
+import org.apache.lucene.spatial3d.geom.PlanetModel;
+import org.locationtech.spatial4j.context.SpatialContext;
+import org.locationtech.spatial4j.context.SpatialContextFactory;
+import org.locationtech.spatial4j.distance.DistanceUtils;
+import org.locationtech.spatial4j.exception.InvalidShapeException;
+import org.locationtech.spatial4j.shape.Circle;
+import org.locationtech.spatial4j.shape.Point;
+import org.locationtech.spatial4j.shape.Rectangle;
+import org.locationtech.spatial4j.shape.Shape;
+import org.locationtech.spatial4j.shape.ShapeCollection;
+import org.locationtech.spatial4j.shape.ShapeFactory;
+
+/**
+ * Geo3d implementation of {@link ShapeFactory}
+ *
+ * @lucene.experimental
+ */
+public class Geo3dShapeFactory implements ShapeFactory {
+
+ private final boolean normWrapLongitude;
+ private SpatialContext context;
+ private PlanetModel planetModel;
+
+ @SuppressWarnings("unchecked")
+ public Geo3dShapeFactory(SpatialContext context, SpatialContextFactory factory) {
+ this.context = context;
+ this.planetModel = ((Geo3dSpatialContextFactory) factory).planetModel;
+ this.normWrapLongitude = context.isGeo() && factory.normWrapLongitude;
+ }
+
+ @Override
+ public SpatialContext getSpatialContext() {
+ return context;
+ }
+
+ @Override
+ public boolean isNormWrapLongitude() {
+ return normWrapLongitude;
+ }
+
+ @Override
+ public double normX(double x) {
+ if (this.normWrapLongitude) {
+ x = DistanceUtils.normLonDEG(x);
+ }
+ return x;
+ }
+
+ @Override
+ public double normY(double y) {
+ return y;
+ }
+
+ @Override
+ public double normZ(double z) {
+ return z;
+ }
+
+ @Override
+ public double normDist(double distance) {
+ return distance;
+ }
+
+ @Override
+ public void verifyX(double x) {
+ Rectangle bounds = this.context.getWorldBounds();
+ if (x < bounds.getMinX() || x > bounds.getMaxX()) {
+ throw new InvalidShapeException("Bad X value " + x + " is not in boundary " + bounds);
+ }
+ }
+
+ @Override
+ public void verifyY(double y) {
+ Rectangle bounds = this.context.getWorldBounds();
+ if (y < bounds.getMinY() || y > bounds.getMaxY()) {
+ throw new InvalidShapeException("Bad Y value " + y + " is not in boundary " + bounds);
+ }
+ }
+
+ @Override
+ public void verifyZ(double v) {
+ }
+
+ @Override
+ public Point pointXY(double x, double y) {
+ GeoPointShape point = GeoPointShapeFactory.makeGeoPointShape(planetModel,
+ y * DistanceUtils.DEGREES_TO_RADIANS,
+ x * DistanceUtils.DEGREES_TO_RADIANS);
+ return new Geo3dPointShape(point, context);
+ }
+
+ @Override
+ public Point pointXYZ(double x, double y, double z) {
+ GeoPoint point = new GeoPoint(x, y, z);
+ GeoPointShape pointShape = GeoPointShapeFactory.makeGeoPointShape(planetModel,
+ point.getLatitude(),
+ point.getLongitude());
+ return new Geo3dPointShape(pointShape, context);
+ //throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Rectangle rect(Point point, Point point1) {
+ return rect(point.getX(), point1.getX(), point.getY(), point1.getY());
+ }
+
+ @Override
+ public Rectangle rect(double minX, double maxX, double minY, double maxY) {
+ GeoBBox bBox = GeoBBoxFactory.makeGeoBBox(planetModel,
+ maxY * DistanceUtils.DEGREES_TO_RADIANS,
+ minY * DistanceUtils.DEGREES_TO_RADIANS,
+ minX * DistanceUtils.DEGREES_TO_RADIANS,
+ maxX * DistanceUtils.DEGREES_TO_RADIANS);
+ return new Geo3dRectangleShape(bBox, context, minX, maxX, minY, maxY);
+ }
+
+ @Override
+ public Circle circle(double x, double y, double distance) {
+ GeoCircle circle = GeoCircleFactory.makeGeoCircle(planetModel,
+ y * DistanceUtils.DEGREES_TO_RADIANS,
+ x * DistanceUtils.DEGREES_TO_RADIANS,
+ distance * DistanceUtils.DEGREES_TO_RADIANS);
+ return new Geo3dCircleShape(circle, context);
+ }
+
+ @Override
+ public Circle circle(Point point, double distance) {
+ return circle(point.getX(), point.getY(), distance);
+ }
+
+ @Override
+ public Shape lineString(List list, double distance) {
+ LineStringBuilder builder = lineString();
+ for (Point point : list) {
+ builder.pointXY(point.getX(), point.getY());
+ }
+ builder.buffer(distance);
+ return builder.build();
+ }
+
+ @Override
+ public ShapeCollection multiShape(List list) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LineStringBuilder lineString() {
+ return new Geo3dLineStringBuilder();
+ }
+
+ @Override
+ public PolygonBuilder polygon() {
+ return new Geo3dPolygonBuilder();
+ }
+
+ @Override
+ public MultiShapeBuilder multiShape(Class aClass) {
+ return new Geo3dMultiShapeBuilder<>();
+ }
+
+ @Override
+ public MultiPointBuilder multiPoint() {
+ return new Geo3dMultiPointBuilder();
+ }
+
+ @Override
+ public MultiLineStringBuilder multiLineString() {
+ return new Geo3dMultiLineBuilder();
+ }
+
+ @Override
+ public MultiPolygonBuilder multiPolygon() {
+ return new Geo3dMultiPolygonBuilder();
+ }
+
+ /**
+ * Geo3d implementation of {@link org.locationtech.spatial4j.shape.ShapeFactory.PointsBuilder} interface to
+ * generate {@link GeoPoint}.
+ *
+ * @param is normally this object
+ */
+ private class Geo3dPointBuilder implements PointsBuilder {
+
+ List points = new ArrayList<>();
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T pointXY(double x, double y) {
+ GeoPoint point = new GeoPoint(planetModel, y * DistanceUtils.DEGREES_TO_RADIANS, x * DistanceUtils.DEGREES_TO_RADIANS);
+ points.add(point);
+ return (T) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T pointXYZ(double x, double y, double z) {
+ GeoPoint point = new GeoPoint(x, y, z);
+ if (!points.contains(point)) {
+ points.add(point);
+ }
+ return (T) this;
+ }
+ }
+
+ /**
+ * Geo3d implementation of {@link org.locationtech.spatial4j.shape.ShapeFactory.LineStringBuilder} to generate
+ * nine Strings. Note that GeoPath needs a buffer so we set the
+ * buffer to 1e-10.
+ */
+ private class Geo3dLineStringBuilder extends Geo3dPointBuilder implements LineStringBuilder {
+
+ double distance = 0;
+
+ @Override
+ public LineStringBuilder buffer(double distance) {
+ this.distance = distance;
+ return this;
+ }
+
+ @Override
+ public Shape build() {
+ GeoPath path = GeoPathFactory.makeGeoPath(planetModel, distance, points.toArray(new GeoPoint[points.size()]));
+ return new Geo3dShape<>(path, context);
+ }
+ }
+
+ /**
+ * Geo3d implementation of {@link org.locationtech.spatial4j.shape.ShapeFactory.PolygonBuilder} to generate
+ * polygons.
+ */
+ private class Geo3dPolygonBuilder extends Geo3dPointBuilder implements PolygonBuilder {
+
+ List polyHoles;
+
+ @Override
+ public HoleBuilder hole() {
+ return new Geo3dHoleBuilder();
+ }
+
+ class Geo3dHoleBuilder extends Geo3dPointBuilder implements PolygonBuilder.HoleBuilder {
+ @Override
+ public PolygonBuilder endHole() {
+ if (polyHoles == null) {
+ polyHoles = new ArrayList<>();
+ }
+ polyHoles.add(GeoPolygonFactory.makeGeoPolygon(planetModel, points));
+ return Geo3dPolygonBuilder.this;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Shape build() {
+ GeoPolygon polygon = GeoPolygonFactory.makeGeoPolygon(planetModel, points, polyHoles);
+ return new Geo3dShape<>(polygon, context);
+ }
+
+ @Override
+ public Shape buildOrRect() {
+ return build();
+ }
+ }
+
+ private class Geo3dMultiPointBuilder extends Geo3dPointBuilder implements MultiPointBuilder {
+
+ @Override
+ public Shape build() {
+ GeoCompositeAreaShape areaShape = new GeoCompositeAreaShape(planetModel);
+ for (GeoPoint point : points) {
+ GeoPointShape pointShape = GeoPointShapeFactory.makeGeoPointShape(planetModel, point.getLatitude(), point.getLongitude());
+ areaShape.addShape(pointShape);
+ }
+ return new Geo3dShape<>(areaShape, context);
+ }
+ }
+
+ /**
+ * Geo3d implementation of {@link org.locationtech.spatial4j.shape.ShapeFactory.MultiLineStringBuilder} to generate
+ * multi-lines
+ */
+ private class Geo3dMultiLineBuilder implements MultiLineStringBuilder {
+
+ List builders = new ArrayList<>();
+
+ @Override
+ public LineStringBuilder lineString() {
+ return new Geo3dLineStringBuilder();
+ }
+
+ @Override
+ public MultiLineStringBuilder add(LineStringBuilder lineStringBuilder) {
+ builders.add(lineStringBuilder);
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Shape build() {
+ GeoCompositeAreaShape areaShape = new GeoCompositeAreaShape(planetModel);
+ for (LineStringBuilder builder : builders) {
+ Geo3dShape shape = (Geo3dShape) builder.build();
+ areaShape.addShape(shape.shape);
+ }
+ return new Geo3dShape<>(areaShape, context);
+ }
+ }
+
+ /**
+ * Geo3d implementation of {@link org.locationtech.spatial4j.shape.ShapeFactory.MultiPolygonBuilder} to generate
+ * multi-polygons. We have chosen to use a composite shape but
+ * it might be possible to use GeoComplexPolygon.
+ */
+ private class Geo3dMultiPolygonBuilder implements MultiPolygonBuilder {
+
+ List builders = new ArrayList<>();
+
+ @Override
+ public PolygonBuilder polygon() {
+ return new Geo3dPolygonBuilder();
+ }
+
+ @Override
+ public MultiPolygonBuilder add(PolygonBuilder polygonBuilder) {
+ builders.add(polygonBuilder);
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Shape build() {
+ GeoCompositeAreaShape areaShape = new GeoCompositeAreaShape(planetModel);
+ for (PolygonBuilder builder : builders) {
+ Geo3dShape shape = (Geo3dShape) builder.build();
+ areaShape.addShape(shape.shape);
+ }
+ return new Geo3dShape<>(areaShape, context);
+ }
+ }
+
+ /**
+ * Geo3d implementation of {@link org.locationtech.spatial4j.shape.ShapeFactory.MultiShapeBuilder} to generate
+ * geometry collections
+ *
+ * @param is the type of shapes.
+ */
+ private class Geo3dMultiShapeBuilder implements MultiShapeBuilder {
+
+ GeoCompositeAreaShape composite = new GeoCompositeAreaShape(planetModel);
+
+ @Override
+ public MultiShapeBuilder add(T shape) {
+ Geo3dShape> areaShape = (Geo3dShape>) shape;
+ composite.addShape(areaShape.shape);
+ return this;
+ }
+
+ @Override
+ public Shape build() {
+ return new Geo3dShape<>(composite, context);
+ }
+ }
+}
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dSpatialContextFactory.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dSpatialContextFactory.java
new file mode 100644
index 00000000000..1ce4ebce67a
--- /dev/null
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/spatial4j/Geo3dSpatialContextFactory.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 java.util.Map;
+
+import org.apache.lucene.spatial3d.geom.PlanetModel;
+import org.locationtech.spatial4j.context.SpatialContext;
+import org.locationtech.spatial4j.context.SpatialContextFactory;
+
+/**
+ * Geo3d implementation of {@link SpatialContextFactory}
+ *
+ * @lucene.experimental
+ */
+public class Geo3dSpatialContextFactory extends SpatialContextFactory {
+
+ /**
+ * The default planet model
+ */
+ private static final PlanetModel DEFAULT_PLANET_MODEL = PlanetModel.SPHERE;
+
+ /**
+ * The planet model
+ */
+ public PlanetModel planetModel;
+
+ /**
+ * Empty Constructor.
+ */
+ public Geo3dSpatialContextFactory() {
+ this.binaryCodecClass = Geo3dBinaryCodec.class;
+ this.shapeFactoryClass = Geo3dShapeFactory.class;
+ }
+
+ @Override
+ public SpatialContext newSpatialContext() {
+ if (planetModel == null) {
+ planetModel = DEFAULT_PLANET_MODEL;
+ }
+ if (distCalc == null) {
+ this.distCalc = new Geo3dDistanceCalculator(planetModel);
+ }
+ return new SpatialContext(this);
+ }
+
+ @Override
+ protected void init(Map args, ClassLoader classLoader) {
+ initPlanetModel(args);
+ super.init(args, classLoader);
+ }
+
+ protected void initPlanetModel(Map args) {
+ String planetModel = args.get("planetModel");
+ if (planetModel != null) {
+ if (planetModel.equalsIgnoreCase("sphere")) {
+ this.planetModel = PlanetModel.SPHERE;
+ } else if (planetModel.equalsIgnoreCase("wgs84")) {
+ this.planetModel = PlanetModel.WGS84;
+ } else {
+ throw new RuntimeException("Unknown planet model: " + planetModel);
+ }
+ } else {
+ this.planetModel = DEFAULT_PLANET_MODEL;
+ }
+ }
+
+ @Override
+ protected void initCalculator() {
+ String calcStr = this.args.get("distCalculator");
+ if (calcStr == null) {
+ return;
+ } else if (calcStr.equals("geo3d")) {
+ this.distCalc = new Geo3dDistanceCalculator(planetModel);
+ } else {
+ super.initCalculator(); // some other distance calculator
+ }
+ }
+}
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/SpatialArgsTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/SpatialArgsTest.java
index 094953a95db..da351fc857e 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/SpatialArgsTest.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/SpatialArgsTest.java
@@ -16,18 +16,18 @@
*/
package org.apache.lucene.spatial;
+import org.apache.lucene.spatial.query.SpatialArgs;
+import org.apache.lucene.spatial.spatial4j.Geo3dSpatialContextFactory;
+import org.apache.lucene.util.LuceneTestCase;
+import org.junit.Test;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.shape.Shape;
-import org.apache.lucene.spatial.query.SpatialArgs;
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-
-public class SpatialArgsTest {
+public class SpatialArgsTest extends LuceneTestCase {
@Test
public void calcDistanceFromErrPct() {
- final SpatialContext ctx = SpatialContext.GEO;
+ final SpatialContext ctx = usually() ? SpatialContext.GEO : new Geo3dSpatialContextFactory().newSpatialContext();
final double DEP = 0.5;//distErrPct
//the result is the diagonal distance from the center to the closest corner,
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
index 26448b6bf09..0b2968465f7 100644
--- 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
@@ -28,16 +28,15 @@ 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.spatial3d.geom.GeoBBoxFactory;
-import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
+import org.apache.lucene.spatial3d.geom.GeoAreaShape;
+import org.apache.lucene.spatial3d.geom.GeoPath;
import org.apache.lucene.spatial3d.geom.GeoPathFactory;
import org.apache.lucene.spatial3d.geom.GeoPoint;
import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
-import org.apache.lucene.spatial3d.geom.GeoShape;
import org.apache.lucene.spatial3d.geom.PlanetModel;
+import org.apache.lucene.spatial3d.geom.RandomGeo3dShapeGenerator;
import org.junit.Test;
import org.locationtech.spatial4j.context.SpatialContext;
-import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Rectangle;
import org.locationtech.spatial4j.shape.Shape;
@@ -45,11 +44,10 @@ import static org.locationtech.spatial4j.distance.DistanceUtils.DEGREES_TO_RADIA
public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
+ private PlanetModel planetModel;
+ private RandomGeo3dShapeGenerator shapeGenerator;
private SpatialPrefixTree grid;
private RecursivePrefixTreeStrategy rptStrategy;
- {
- this.ctx = SpatialContext.GEO;
- }
private void setupGeohashGrid() {
this.grid = new GeohashPrefixTree(ctx, 2);//A fairly shallow grid
@@ -64,7 +62,12 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
}
private void setupStrategy() {
- //setup
+ shapeGenerator = new RandomGeo3dShapeGenerator();
+ planetModel = shapeGenerator.randomPlanetModel();
+ Geo3dSpatialContextFactory factory = new Geo3dSpatialContextFactory();
+ factory.planetModel = planetModel;
+ ctx = factory.newSpatialContext();
+
setupGeohashGrid();
SerializedDVStrategy serializedDVStrategy = new SerializedDVStrategy(ctx, getClass().getSimpleName() + "_sdv");
@@ -76,12 +79,12 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
public void testFailure1() throws IOException {
setupStrategy();
final List 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));
+ points.add(new GeoPoint(planetModel, 18 * DEGREES_TO_RADIANS, -27 * DEGREES_TO_RADIANS));
+ points.add(new GeoPoint(planetModel, -57 * DEGREES_TO_RADIANS, 146 * DEGREES_TO_RADIANS));
+ points.add(new GeoPoint(planetModel, 14 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS));
+ points.add(new GeoPoint(planetModel, -15 * DEGREES_TO_RADIANS, 153 * DEGREES_TO_RADIANS));
- final Shape triangle = new Geo3dShape(GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points),ctx);
+ final Shape triangle = new Geo3dShape(GeoPolygonFactory.makeGeoPolygon(planetModel, points),ctx);
final Rectangle rect = ctx.makeRectangle(-49, -45, 73, 86);
testOperation(rect,SpatialOperation.Intersects,triangle, false);
}
@@ -91,16 +94,16 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
setupStrategy();
final List 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));
+ points.add(new GeoPoint(planetModel, 18 * DEGREES_TO_RADIANS, -27 * DEGREES_TO_RADIANS));
+ points.add(new GeoPoint(planetModel, -57 * DEGREES_TO_RADIANS, 146 * DEGREES_TO_RADIANS));
+ points.add(new GeoPoint(planetModel, 14 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS));
+ points.add(new GeoPoint(planetModel, -15 * DEGREES_TO_RADIANS, 153 * DEGREES_TO_RADIANS));
final GeoPoint[] pathPoints = new GeoPoint[] {
- new GeoPoint(PlanetModel.SPHERE, 55.0 * DEGREES_TO_RADIANS, -26.0 * DEGREES_TO_RADIANS),
- new GeoPoint(PlanetModel.SPHERE, -90.0 * DEGREES_TO_RADIANS, 0.0),
- new GeoPoint(PlanetModel.SPHERE, 54.0 * DEGREES_TO_RADIANS, 165.0 * DEGREES_TO_RADIANS),
- new GeoPoint(PlanetModel.SPHERE, -90.0 * DEGREES_TO_RADIANS, 0.0)};
- final GeoShape path = GeoPathFactory.makeGeoPath(PlanetModel.SPHERE, 29 * DEGREES_TO_RADIANS, pathPoints);
+ new GeoPoint(planetModel, 55.0 * DEGREES_TO_RADIANS, -26.0 * DEGREES_TO_RADIANS),
+ new GeoPoint(planetModel, -90.0 * DEGREES_TO_RADIANS, 0.0),
+ new GeoPoint(planetModel, 54.0 * DEGREES_TO_RADIANS, 165.0 * DEGREES_TO_RADIANS),
+ new GeoPoint(planetModel, -90.0 * DEGREES_TO_RADIANS, 0.0)};
+ final GeoPath path = GeoPathFactory.makeGeoPath(planetModel, 29 * DEGREES_TO_RADIANS, pathPoints);
final Shape shape = new Geo3dShape(path,ctx);
final Rectangle rect = ctx.makeRectangle(131, 143, 39, 54);
testOperation(rect,SpatialOperation.Intersects,shape,true);
@@ -114,111 +117,57 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
testOperationRandomShapes(SpatialOperation.Intersects);
}
- private Shape makeTriangle(double x1, double y1, double x2, double y2, double x3, double y3) {
- final List 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 GeoShape shape = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, geoPoints);
- return new Geo3dShape(shape, ctx);
- }
-
@Override
protected Shape randomIndexedShape() {
- return randomRectangle();
+ int type = shapeGenerator.randomShapeType();
+ GeoAreaShape areaShape = shapeGenerator.randomGeoAreaShape(type, planetModel);
+ return new Geo3dShape<>(areaShape, ctx);
}
@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 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);
- if (shape == null) {
- continue;
- }
- 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 = GeoCircleFactory.makeGeoCircle(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;
- final GeoPoint[] points = new GeoPoint[pointCount];
- while (true) {
- for (int i = 0; i < pointCount; i++) {
- final Point nextPoint = randomPoint();
- points[i] = new GeoPoint(PlanetModel.SPHERE, nextPoint.getY() * DEGREES_TO_RADIANS, nextPoint.getX() * DEGREES_TO_RADIANS);
- }
- try {
- final GeoShape path = GeoPathFactory.makeGeoPath(PlanetModel.SPHERE, width, points);
- 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");
- }
+ int type = shapeGenerator.randomShapeType();
+ GeoAreaShape areaShape = shapeGenerator.randomGeoAreaShape(type, planetModel);
+ return new Geo3dShape<>(areaShape, ctx);
+ }
+
+ //TODO move to a new test class?
+ @Test
+ public void testWKT() throws Exception {
+ Geo3dSpatialContextFactory factory = new Geo3dSpatialContextFactory();
+ SpatialContext ctx = factory.newSpatialContext();
+ String wkt = "POLYGON ((20.0 -60.4, 20.1 -60.4, 20.1 -60.3, 20.0 -60.3,20.0 -60.4))";
+ Shape s = ctx.getFormats().getWktReader().read(wkt);
+ assertTrue(s instanceof Geo3dShape>);
+ wkt = "POINT (30 10)";
+ s = ctx.getFormats().getWktReader().read(wkt);
+ assertTrue(s instanceof Geo3dShape>);
+ wkt = "LINESTRING (30 10, 10 30, 40 40)";
+ s = ctx.getFormats().getWktReader().read(wkt);
+ assertTrue(s instanceof Geo3dShape>);
+ wkt = "POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))";
+ s = ctx.getFormats().getWktReader().read(wkt);
+ assertTrue(s instanceof Geo3dShape>);
+ wkt = "MULTIPOINT ((10 40), (40 30), (20 20), (30 10))";
+ s = ctx.getFormats().getWktReader().read(wkt);
+ assertTrue(s instanceof Geo3dShape>);
+ wkt = "MULTILINESTRING ((10 10, 20 20, 10 40),(40 40, 30 30, 40 20, 30 10))";
+ s = ctx.getFormats().getWktReader().read(wkt);
+ assertTrue(s instanceof Geo3dShape>);
+ wkt = "MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 20)))";
+ s = ctx.getFormats().getWktReader().read(wkt);
+ assertTrue(s instanceof Geo3dShape>);
+ wkt = "GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6,7 10))";
+ s = ctx.getFormats().getWktReader().read(wkt);
+ assertTrue(s instanceof Geo3dShape>);
+ wkt = "ENVELOPE(1, 2, 4, 3)";
+ s = ctx.getFormats().getWktReader().read(wkt);
+ assertTrue(s instanceof Geo3dShape>);
+ wkt = "BUFFER(POINT(-10 30), 5.2)";
+ s = ctx.getFormats().getWktReader().read(wkt);
+ assertTrue(s instanceof Geo3dShape>);
+ //wkt = "BUFFER(LINESTRING(1 2, 3 4), 0.5)";
+ //s = ctx.getFormats().getWktReader().read(wkt);
+ //assertTrue(s instanceof Geo3dShape>);
}
}
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
index af31120356b..9873012f6b0 100644
--- 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
@@ -19,6 +19,8 @@ package org.apache.lucene.spatial.spatial4j;
import java.util.ArrayList;
import java.util.List;
+import org.apache.lucene.spatial3d.geom.GeoPath;
+import org.apache.lucene.spatial3d.geom.GeoPolygon;
import org.locationtech.spatial4j.TestLog;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.distance.DistanceUtils;
@@ -119,9 +121,9 @@ public abstract class Geo3dShapeRectRelationTestCase extends RandomizedShapeTest
protected Geo3dShape generateRandomShape(Point nearP) {
final int circleRadius = 180 - random().nextInt(180);//no 0-radius
final Point point = nearP;
- final GeoShape shape = GeoCircleFactory.makeGeoCircle(planetModel, point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS,
+ final GeoCircle shape = GeoCircleFactory.makeGeoCircle(planetModel, point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS,
circleRadius * DEGREES_TO_RADIANS);
- return new Geo3dShape(planetModel, shape, ctx);
+ return new Geo3dShape(shape, ctx);
}
@Override
@@ -153,11 +155,11 @@ public abstract class Geo3dShapeRectRelationTestCase extends RandomizedShapeTest
ulhcPoint = lrhcPoint;
lrhcPoint = temp;
}
- final GeoShape shape = GeoBBoxFactory.makeGeoBBox(planetModel, ulhcPoint.getY() * DEGREES_TO_RADIANS,
+ final GeoBBox 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);
+ return new Geo3dShape(shape, ctx);
}
@Override
@@ -185,11 +187,11 @@ public abstract class Geo3dShapeRectRelationTestCase extends RandomizedShapeTest
geoPoints.add(gPt);
}
try {
- final GeoShape shape = GeoPolygonFactory.makeGeoPolygon(planetModel, geoPoints);
+ final GeoPolygon shape = GeoPolygonFactory.makeGeoPolygon(planetModel, geoPoints);
if (shape == null) {
continue;
}
- return new Geo3dShape(planetModel, shape, ctx);
+ 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.
@@ -231,8 +233,8 @@ public abstract class Geo3dShapeRectRelationTestCase extends RandomizedShapeTest
}
try {
- final GeoShape path = GeoPathFactory.makeGeoPath(planetModel, width, points);
- return new Geo3dShape(planetModel, path, ctx);
+ final GeoPath path = GeoPathFactory.makeGeoPath(planetModel, width, points);
+ 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.
@@ -257,6 +259,6 @@ public abstract class Geo3dShapeRectRelationTestCase extends RandomizedShapeTest
private Point geoPointToSpatial4jPoint(GeoPoint geoPoint) {
return ctx.makePoint(geoPoint.getLongitude() * DistanceUtils.RADIANS_TO_DEGREES,
- geoPoint.getLongitude() * DistanceUtils.RADIANS_TO_DEGREES);
+ geoPoint.getLatitude() * DistanceUtils.RADIANS_TO_DEGREES);
}
}
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
index 19e3912a833..20db21c61c3 100644
--- 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
@@ -19,21 +19,29 @@ package org.apache.lucene.spatial.spatial4j;
import java.util.ArrayList;
import java.util.List;
-import org.locationtech.spatial4j.shape.Rectangle;
import org.apache.lucene.spatial3d.geom.GeoArea;
import org.apache.lucene.spatial3d.geom.GeoBBox;
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
+import org.apache.lucene.spatial3d.geom.GeoCircle;
import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
import org.apache.lucene.spatial3d.geom.GeoPoint;
import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
import org.apache.lucene.spatial3d.geom.GeoShape;
import org.apache.lucene.spatial3d.geom.PlanetModel;
import org.junit.Test;
+import org.locationtech.spatial4j.shape.Circle;
+import org.locationtech.spatial4j.shape.Point;
+import org.locationtech.spatial4j.shape.Rectangle;
+import org.locationtech.spatial4j.shape.SpatialRelation;
public class Geo3dShapeSphereModelRectRelationTest extends Geo3dShapeRectRelationTestCase {
public Geo3dShapeSphereModelRectRelationTest() {
super(PlanetModel.SPHERE);
+ Geo3dSpatialContextFactory factory = new Geo3dSpatialContextFactory();
+ factory.planetModel = PlanetModel.SPHERE;
+ //factory.distCalc = new GeodesicSphereDistCalc.Haversine();
+ this.ctx = factory.newSpatialContext();
}
@Test
@@ -60,13 +68,28 @@ public class Geo3dShapeSphereModelRectRelationTest extends Geo3dShapeRectRelatio
@Test
public void testFailure2_LUCENE6475() {
- GeoShape geo3dCircle = GeoCircleFactory.makeGeoCircle(planetModel, 1.6282053147165243E-4 * RADIANS_PER_DEGREE,
+ GeoCircle geo3dCircle = GeoCircleFactory.makeGeoCircle(planetModel, 1.6282053147165243E-4 * RADIANS_PER_DEGREE,
-70.1600629789353 * RADIANS_PER_DEGREE, 86 * RADIANS_PER_DEGREE);
- Geo3dShape geo3dShape = new Geo3dShape(planetModel, geo3dCircle, ctx);
+ 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());
}
+
+ @Test
+ public void pointBearingTest(){
+ double radius = 136;
+ double distance = 135.97;
+ double bearing = 188;
+ Point p = ctx.getShapeFactory().pointXY(35, 85);
+ Circle circle = ctx.getShapeFactory().circle(p, radius);
+ Point bPoint = ctx.getDistCalc().pointOnBearing(p, distance, bearing, ctx, (Point) null);
+
+ double d = ctx.getDistCalc().distance(p, bPoint);
+ assertEquals(d, distance, 10-8);
+
+ assertEquals(circle.relate(bPoint), SpatialRelation.CONTAINS);
+ }
}
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
index 43b88ca5912..22d7bd4cbdb 100644
--- 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
@@ -26,11 +26,18 @@ import org.apache.lucene.spatial3d.geom.GeoPath;
import org.apache.lucene.spatial3d.geom.GeoPoint;
import org.apache.lucene.spatial3d.geom.PlanetModel;
import org.junit.Test;
+import org.locationtech.spatial4j.shape.Circle;
+import org.locationtech.spatial4j.shape.Point;
+import org.locationtech.spatial4j.shape.SpatialRelation;
public class Geo3dShapeWGS84ModelRectRelationTest extends Geo3dShapeRectRelationTestCase {
public Geo3dShapeWGS84ModelRectRelationTest() {
super(PlanetModel.WGS84);
+ Geo3dSpatialContextFactory factory = new Geo3dSpatialContextFactory();
+ factory.planetModel = PlanetModel.WGS84;
+ //factory.distCalc = new GeodesicSphereDistCalc.Haversine();
+ this.ctx = factory.newSpatialContext();
}
@Test
@@ -92,4 +99,18 @@ public class Geo3dShapeWGS84ModelRectRelationTest extends Geo3dShapeRectRelation
// (3) The point mentioned is NOT inside the path segment, either. (I think it should be...)
}
+ @Test
+ public void pointBearingTest(){
+ double radius = 136;
+ double distance = 135.97;
+ double bearing = 188;
+ Point p = ctx.getShapeFactory().pointXY(35, 85);
+ Circle circle = ctx.getShapeFactory().circle(p, radius);
+ Point bPoint = ctx.getDistCalc().pointOnBearing(p, distance, bearing, ctx, (Point) null);
+
+ double d = ctx.getDistCalc().distance(p, bPoint);
+ assertEquals(d, distance, 10-8);
+
+ assertEquals(circle.relate(bPoint), SpatialRelation.CONTAINS);
+ }
}