mirror of https://github.com/apache/lucene.git
LUCENE-7951: Spatial4j implementations using Geo3d
This commit is contained in:
parent
171b3739ba
commit
035523ba7a
|
@ -45,6 +45,13 @@
|
|||
<artifactId>lucene-test-framework</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.lucene</groupId>
|
||||
<artifactId>lucene-spatial3d</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
@lucene-spatial-extras.internal.dependencies@
|
||||
@lucene-spatial-extras.external.dependencies@
|
||||
@lucene-spatial-extras.internal.test.dependencies@
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -38,10 +38,19 @@
|
|||
<path refid="test.base.classpath" />
|
||||
<path refid="spatialjar"/>
|
||||
<pathelement path="src/test-files" />
|
||||
<pathelement path="${common.dir}/build/spatial3d/classes/test" />
|
||||
</path>
|
||||
|
||||
<target name="compile-core" depends="jar-spatial3d,common.compile-core" />
|
||||
|
||||
<target name="compile-test" depends="compile-spatial3d-tests,common.compile-test" />
|
||||
|
||||
<target name="compile-spatial3d-tests">
|
||||
<ant dir="${common.dir}/spatial3d" target="compile-test" inheritAll="false">
|
||||
<propertyset refid="uptodate.and.compiled.properties"/>
|
||||
</ant>
|
||||
</target>
|
||||
|
||||
<target name="javadocs" depends="javadocs-spatial3d,compile-core,check-javadocs-uptodate"
|
||||
unless="javadocs-uptodate-${name}">
|
||||
<invoke-module-javadoc>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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<GeoCircle> 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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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<GeoPointShape> 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;
|
||||
}
|
||||
}
|
|
@ -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<GeoBBox> 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);
|
||||
}
|
||||
}
|
|
@ -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 <T> 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<T extends GeoAreaShape> 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());
|
||||
}
|
||||
|
||||
protected SpatialRelation relate(Rectangle r) {
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Point> list, double distance) {
|
||||
LineStringBuilder builder = lineString();
|
||||
for (Point point : list) {
|
||||
builder.pointXY(point.getX(), point.getY());
|
||||
}
|
||||
builder.buffer(distance);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends Shape> ShapeCollection<S> multiShape(List<S> list) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineStringBuilder lineString() {
|
||||
return new Geo3dLineStringBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PolygonBuilder polygon() {
|
||||
return new Geo3dPolygonBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Shape> MultiShapeBuilder<T> multiShape(Class<T> 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 <T> is normally this object
|
||||
*/
|
||||
private class Geo3dPointBuilder<T> implements PointsBuilder<T> {
|
||||
|
||||
List<GeoPoint> 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<LineStringBuilder> 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<PolygonBuilder> implements PolygonBuilder {
|
||||
|
||||
List<GeoPolygon> polyHoles;
|
||||
|
||||
@Override
|
||||
public HoleBuilder hole() {
|
||||
return new Geo3dHoleBuilder();
|
||||
}
|
||||
|
||||
class Geo3dHoleBuilder extends Geo3dPointBuilder<PolygonBuilder.HoleBuilder> 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<MultiPointBuilder> 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<LineStringBuilder> 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<GeoPolygon> shape = (Geo3dShape<GeoPolygon>) 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<PolygonBuilder> 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<GeoPolygon> shape = (Geo3dShape<GeoPolygon>) 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 <T> is the type of shapes.
|
||||
*/
|
||||
private class Geo3dMultiShapeBuilder<T extends Shape> implements MultiShapeBuilder<T> {
|
||||
|
||||
GeoCompositeAreaShape composite = new GeoCompositeAreaShape(planetModel);
|
||||
|
||||
@Override
|
||||
public MultiShapeBuilder<T> add(T shape) {
|
||||
Geo3dShape<?> areaShape = (Geo3dShape<?>) shape;
|
||||
composite.addShape(areaShape.shape);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape build() {
|
||||
return new Geo3dShape<>(composite, context);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<String, String> args, ClassLoader classLoader) {
|
||||
initPlanetModel(args);
|
||||
super.init(args, classLoader);
|
||||
}
|
||||
|
||||
protected void initPlanetModel(Map<String, String> 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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<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));
|
||||
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<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));
|
||||
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<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 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<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);
|
||||
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<?>);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue