LUCENE-7072: always use WGS84 planet model in Geo3DPoint

This commit is contained in:
Mike McCandless 2016-03-07 05:22:40 -05:00
parent 6ce6c01318
commit b79067a456
5 changed files with 52 additions and 54 deletions

View File

@ -125,6 +125,9 @@ API Changes
* LUCENE-7064: MultiPhraseQuery is now immutable and should be constructed
with MultiPhraseQuery.Builder. (Luc Vanlerberghe via Adrien Grand)
* LUCENE-7072: Geo3DPoint always uses WGS84 planet model.
(Robert Muir, Mike McCandless)
Optimizations
* LUCENE-6891: Use prefix coding when writing points in

View File

@ -34,6 +34,11 @@ public abstract class BasePlanetObject {
public BasePlanetObject(final PlanetModel planetModel) {
this.planetModel = planetModel;
}
/** Returns the {@link PlanetModel} provided when this shape was created. */
public PlanetModel getPlanetModel() {
return planetModel;
}
@Override
public int hashCode() {

View File

@ -36,8 +36,6 @@ import org.apache.lucene.util.RamUsageEstimator;
* @lucene.experimental */
public final class Geo3DPoint extends Field {
private final PlanetModel planetModel;
/** Indexing {@link FieldType}. */
public static final FieldType TYPE = new FieldType();
static {
@ -46,16 +44,15 @@ public final class Geo3DPoint extends Field {
}
/**
* Creates a new Geo3DPoint field with the specified lat, lon (in radians), given a planet model.
* Creates a new Geo3DPoint field with the specified lat, lon (in radians).
*
* @throws IllegalArgumentException if the field name is null or lat or lon are out of bounds
*/
public Geo3DPoint(String name, PlanetModel planetModel, double lat, double lon) {
public Geo3DPoint(String name, double lat, double lon) {
super(name, TYPE);
this.planetModel = planetModel;
// Translate lat/lon to x,y,z:
final GeoPoint point = new GeoPoint(planetModel, lat, lon);
fillFieldsData(planetModel, point.x, point.y, point.z);
final GeoPoint point = new GeoPoint(PlanetModel.WGS84, lat, lon);
fillFieldsData(point.x, point.y, point.z);
}
/**
@ -63,40 +60,38 @@ public final class Geo3DPoint extends Field {
*
* @throws IllegalArgumentException if the field name is null or lat or lon are out of bounds
*/
public Geo3DPoint(String name, PlanetModel planetModel, double x, double y, double z) {
public Geo3DPoint(String name, double x, double y, double z) {
super(name, TYPE);
this.planetModel = planetModel;
fillFieldsData(planetModel, x, y, z);
fillFieldsData(x, y, z);
}
private void fillFieldsData(PlanetModel planetModel, double x, double y, double z) {
private void fillFieldsData(double x, double y, double z) {
byte[] bytes = new byte[12];
encodeDimension(planetModel, x, bytes, 0);
encodeDimension(planetModel, y, bytes, Integer.BYTES);
encodeDimension(planetModel, z, bytes, 2*Integer.BYTES);
encodeDimension(x, bytes, 0);
encodeDimension(y, bytes, Integer.BYTES);
encodeDimension(z, bytes, 2*Integer.BYTES);
fieldsData = new BytesRef(bytes);
}
// public helper methods (e.g. for queries)
/** Encode single dimension */
public static void encodeDimension(PlanetModel planetModel, double value, byte bytes[], int offset) {
NumericUtils.intToSortableBytes(Geo3DUtil.encodeValue(planetModel.getMaximumMagnitude(), value), bytes, offset);
public static void encodeDimension(double value, byte bytes[], int offset) {
NumericUtils.intToSortableBytes(Geo3DUtil.encodeValue(PlanetModel.WGS84.getMaximumMagnitude(), value), bytes, offset);
}
/** Decode single dimension */
public static double decodeDimension(PlanetModel planetModel, byte value[], int offset) {
return Geo3DUtil.decodeValueCenter(planetModel.getMaximumMagnitude(), NumericUtils.sortableBytesToInt(value, offset));
public static double decodeDimension(byte value[], int offset) {
return Geo3DUtil.decodeValueCenter(PlanetModel.WGS84.getMaximumMagnitude(), NumericUtils.sortableBytesToInt(value, offset));
}
/** Returns a query matching all points inside the provided shape.
*
* @param planetModel The {@link PlanetModel} to use, which must match what was used during indexing
* @param field field name. must not be {@code null}.
* @param shape Which {@link GeoShape} to match
*/
public static Query newShapeQuery(PlanetModel planetModel, String field, GeoShape shape) {
return new PointInGeo3DShapeQuery(planetModel, field, shape);
public static Query newShapeQuery(String field, GeoShape shape) {
return new PointInGeo3DShapeQuery(field, shape);
}
@Override
@ -108,9 +103,9 @@ public final class Geo3DPoint extends Field {
result.append(':');
BytesRef bytes = (BytesRef) fieldsData;
result.append(" x=" + decodeDimension(planetModel, bytes.bytes, bytes.offset));
result.append(" y=" + decodeDimension(planetModel, bytes.bytes, bytes.offset + Integer.BYTES));
result.append(" z=" + decodeDimension(planetModel, bytes.bytes, bytes.offset + 2*Integer.BYTES));
result.append(" x=" + decodeDimension(bytes.bytes, bytes.offset));
result.append(" y=" + decodeDimension(bytes.bytes, bytes.offset + Integer.BYTES));
result.append(" z=" + decodeDimension(bytes.bytes, bytes.offset + 2*Integer.BYTES));
result.append('>');
return result.toString();
}

View File

@ -40,14 +40,19 @@ import org.apache.lucene.util.NumericUtils;
class PointInGeo3DShapeQuery extends Query {
final String field;
final PlanetModel planetModel;
final GeoShape shape;
/** The lats/lons must be clockwise or counter-clockwise. */
public PointInGeo3DShapeQuery(PlanetModel planetModel, String field, GeoShape shape) {
public PointInGeo3DShapeQuery(String field, GeoShape shape) {
this.field = field;
this.planetModel = planetModel;
this.shape = shape;
if (shape instanceof BasePlanetObject) {
BasePlanetObject planetObject = (BasePlanetObject) shape;
if (planetObject.getPlanetModel().equals(PlanetModel.WGS84) == false) {
throw new IllegalArgumentException("this qurey requires PlanetModel.WGS84, but got: " + planetObject.getPlanetModel());
}
}
}
@Override
@ -88,7 +93,7 @@ class PointInGeo3DShapeQuery extends Query {
assert xyzSolid.getRelationship(shape) == GeoArea.WITHIN || xyzSolid.getRelationship(shape) == GeoArea.OVERLAPS: "expected WITHIN (1) or OVERLAPS (2) but got " + xyzSolid.getRelationship(shape) + "; shape="+shape+"; XYZSolid="+xyzSolid;
*/
double planetMax = planetModel.getMaximumMagnitude();
double planetMax = PlanetModel.WGS84.getMaximumMagnitude();
DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc());
@ -103,9 +108,9 @@ class PointInGeo3DShapeQuery extends Query {
@Override
public void visit(int docID, byte[] packedValue) {
assert packedValue.length == 12;
double x = Geo3DPoint.decodeDimension(planetModel, packedValue, 0);
double y = Geo3DPoint.decodeDimension(planetModel, packedValue, Integer.BYTES);
double z = Geo3DPoint.decodeDimension(planetModel, packedValue, 2 * Integer.BYTES);
double x = Geo3DPoint.decodeDimension(packedValue, 0);
double y = Geo3DPoint.decodeDimension(packedValue, Integer.BYTES);
double z = Geo3DPoint.decodeDimension(packedValue, 2 * Integer.BYTES);
if (shape.isWithin(x, y, z)) {
result.add(docID);
}
@ -129,7 +134,7 @@ class PointInGeo3DShapeQuery extends Query {
assert yMin <= yMax;
assert zMin <= zMax;
GeoArea xyzSolid = GeoAreaFactory.makeGeoArea(planetModel, xMin, xMax, yMin, yMax, zMin, zMax);
GeoArea xyzSolid = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84, xMin, xMax, yMin, yMax, zMin, zMax);
switch(xyzSolid.getRelationship(shape)) {
case GeoArea.CONTAINS:
@ -165,10 +170,6 @@ class PointInGeo3DShapeQuery extends Query {
return field;
}
public PlanetModel getPlanetModel() {
return planetModel;
}
public GeoShape getShape() {
return shape;
}
@ -182,13 +183,12 @@ class PointInGeo3DShapeQuery extends Query {
PointInGeo3DShapeQuery that = (PointInGeo3DShapeQuery) o;
return planetModel.equals(that.planetModel) && shape.equals(that.shape);
return shape.equals(that.shape);
}
@Override
public final int hashCode() {
int result = super.hashCode();
result = 31 * result + planetModel.hashCode();
result = 31 * result + shape.hashCode();
return result;
}
@ -203,8 +203,6 @@ class PointInGeo3DShapeQuery extends Query {
sb.append(this.field);
sb.append(':');
}
sb.append(" PlanetModel: ");
sb.append(planetModel);
sb.append(" Shape: ");
sb.append(shape);
return sb.toString();

View File

@ -106,13 +106,12 @@ public class TestGeo3DPoint extends LuceneTestCase {
iwc.setCodec(getCodec());
IndexWriter w = new IndexWriter(dir, iwc);
Document doc = new Document();
doc.add(new Geo3DPoint("field", PlanetModel.WGS84, toRadians(50.7345267), toRadians(-97.5303555)));
doc.add(new Geo3DPoint("field", toRadians(50.7345267), toRadians(-97.5303555)));
w.addDocument(doc);
IndexReader r = DirectoryReader.open(w);
// We can't wrap with "exotic" readers because the query must see the BKD3DDVFormat:
IndexSearcher s = newSearcher(r, false);
assertEquals(1, s.search(Geo3DPoint.newShapeQuery(PlanetModel.WGS84,
"field",
assertEquals(1, s.search(Geo3DPoint.newShapeQuery("field",
GeoCircleFactory.makeGeoCircle(PlanetModel.WGS84, toRadians(50), toRadians(-97), Math.PI/180.)), 1).totalHits);
w.close();
r.close();
@ -640,8 +639,6 @@ public class TestGeo3DPoint extends LuceneTestCase {
private static void verify(double[] lats, double[] lons) throws Exception {
IndexWriterConfig iwc = newIndexWriterConfig();
PlanetModel planetModel = getPlanetModel();
// Else we can get O(N^2) merging:
int mbd = iwc.getMaxBufferedDocs();
if (mbd != -1 && mbd < lats.length/100) {
@ -662,7 +659,7 @@ public class TestGeo3DPoint extends LuceneTestCase {
doc.add(newStringField("id", ""+id, Field.Store.NO));
doc.add(new NumericDocValuesField("id", id));
if (Double.isNaN(lats[id]) == false) {
doc.add(new Geo3DPoint("point", planetModel, lats[id], lons[id]));
doc.add(new Geo3DPoint("point", lats[id], lons[id]));
}
w.addDocument(doc);
if (id > 0 && random().nextInt(100) == 42) {
@ -710,13 +707,13 @@ public class TestGeo3DPoint extends LuceneTestCase {
for (int iter=0;iter<iters && failed.get() == false;iter++) {
GeoShape shape = randomShape(planetModel);
GeoShape shape = randomShape(PlanetModel.WGS84);
if (VERBOSE) {
System.err.println("\n" + Thread.currentThread() + ": TEST: iter=" + iter + " shape="+shape);
}
Query query = Geo3DPoint.newShapeQuery(planetModel, "point", shape);
Query query = Geo3DPoint.newShapeQuery("point", shape);
if (VERBOSE) {
System.err.println(" using query: " + query);
@ -753,10 +750,10 @@ public class TestGeo3DPoint extends LuceneTestCase {
if (Double.isNaN(lats[id]) == false) {
// Accurate point:
GeoPoint point1 = new GeoPoint(planetModel, lats[id], lons[id]);
GeoPoint point1 = new GeoPoint(PlanetModel.WGS84, lats[id], lons[id]);
// Quantized point (32 bits per dim):
GeoPoint point2 = quantize(planetModel.getMaximumMagnitude(), point1);
GeoPoint point2 = quantize(PlanetModel.WGS84.getMaximumMagnitude(), point1);
if (shape.isWithin(point1) != shape.isWithin(point2)) {
if (VERBOSE) {
@ -789,13 +786,13 @@ public class TestGeo3DPoint extends LuceneTestCase {
}
public void testToString() {
Geo3DPoint point = new Geo3DPoint("point", PlanetModel.SPHERE, toRadians(44.244272), toRadians(7.769736));
assertEquals("Geo3DPoint <point: x=0.9242545719837093 y=0.06276412683667808 z=0.37658219569203544>", point.toString());
Geo3DPoint point = new Geo3DPoint("point", toRadians(44.244272), toRadians(7.769736));
assertEquals("Geo3DPoint <point: x=0.9248467864160119 y=0.06280434265368656 z=0.37682349005486243>", point.toString());
}
public void testShapeQueryToString() {
assertEquals("PointInGeo3DShapeQuery: field=point: PlanetModel: PlanetModel.SPHERE Shape: GeoStandardCircle: {planetmodel=PlanetModel.SPHERE, center=[lat=0.3861041107739683, lon=0.06780373760536706], radius=0.1(5.729577951308232)}",
Geo3DPoint.newShapeQuery(PlanetModel.SPHERE, "point", GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, toRadians(44.244272), toRadians(7.769736), 0.1)).toString());
assertEquals("PointInGeo3DShapeQuery: field=point: Shape: GeoStandardCircle: {planetmodel=PlanetModel.WGS84, center=[lat=0.3861041107739683, lon=0.06780373760536706], radius=0.1(5.729577951308232)}",
Geo3DPoint.newShapeQuery("point", GeoCircleFactory.makeGeoCircle(PlanetModel.WGS84, toRadians(44.244272), toRadians(7.769736), 0.1)).toString());
}
private static Directory getDirectory() {