You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by nk...@apache.org on 2016/02/07 05:27:24 UTC
[1/5] lucene-solr git commit: LUCENE-6930: Decouples GeoPointField
from NumericType by using a custom GeoPointTokenStream and TermEnum designed
for GeoPoint prefix terms
Repository: lucene-solr
Updated Branches:
refs/heads/branch_5x aacda06b8 -> cc052491c
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermsEnum.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermsEnum.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermsEnum.java
index 2dd6cb4..626003a 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermsEnum.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermsEnum.java
@@ -16,173 +16,60 @@
*/
package org.apache.lucene.spatial.search;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.apache.lucene.spatial.document.GeoPointField;
import org.apache.lucene.index.FilteredTermsEnum;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
-import org.apache.lucene.util.NumericUtils;
-import org.apache.lucene.spatial.util.GeoRelationUtils;
-import org.apache.lucene.spatial.util.GeoUtils;
+import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
+import org.apache.lucene.spatial.search.GeoPointMultiTermQuery.CellComparator;
/**
- * computes all ranges along a space-filling curve that represents
- * the given bounding box and enumerates all terms contained within those ranges
+ * Base class for {@link GeoPointNumericTermsEnum} and {@link GeoPointPrefixTermsEnum} which compares
+ * candidate GeoPointField encoded terms against terms matching the defined query criteria.
*
* @lucene.experimental
*/
abstract class GeoPointTermsEnum extends FilteredTermsEnum {
- protected final double minLon;
- protected final double minLat;
- protected final double maxLon;
- protected final double maxLat;
-
- protected Range currentRange;
- private final BytesRefBuilder currentCell = new BytesRefBuilder();
- private final BytesRefBuilder nextSubRange = new BytesRefBuilder();
+ protected final short maxShift;
- private final List<Range> rangeBounds = new LinkedList<>();
+ protected BaseRange currentRange;
+ protected BytesRef currentCell;
+ protected final BytesRefBuilder currentCellBRB = new BytesRefBuilder();
+ protected final BytesRefBuilder nextSubRangeBRB = new BytesRefBuilder();
- // detail level should be a factor of PRECISION_STEP limiting the depth of recursion (and number of ranges)
- protected final short DETAIL_LEVEL;
+ protected final CellComparator relationImpl;
- GeoPointTermsEnum(final TermsEnum tenum, final double minLon, final double minLat,
- final double maxLon, final double maxLat) {
+ GeoPointTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
super(tenum);
- final long rectMinHash = GeoUtils.mortonHash(minLon, minLat);
- final long rectMaxHash = GeoUtils.mortonHash(maxLon, maxLat);
- this.minLon = GeoUtils.mortonUnhashLon(rectMinHash);
- this.minLat = GeoUtils.mortonUnhashLat(rectMinHash);
- this.maxLon = GeoUtils.mortonUnhashLon(rectMaxHash);
- this.maxLat = GeoUtils.mortonUnhashLat(rectMaxHash);
- DETAIL_LEVEL = (short)(((GeoUtils.BITS<<1)-computeMaxShift())/2);
-
- computeRange(0L, (short) ((GeoUtils.BITS << 1) - 1));
- assert rangeBounds.isEmpty() == false;
- Collections.sort(rangeBounds);
- }
-
- /**
- * entry point for recursively computing ranges
- */
- private final void computeRange(long term, final short shift) {
- final long split = term | (0x1L<<shift);
- assert shift < 64;
- final long upperMax;
- if (shift < 63) {
- upperMax = term | ((1L << (shift+1))-1);
- } else {
- upperMax = 0xffffffffffffffffL;
- }
- final long lowerMax = split-1;
-
- relateAndRecurse(term, lowerMax, shift);
- relateAndRecurse(split, upperMax, shift);
+ this.maxShift = query.maxShift;
+ this.relationImpl = query.cellComparator;
}
- /**
- * recurse to higher level precision cells to find ranges along the space-filling curve that fall within the
- * query box
- *
- * @param start starting value on the space-filling curve for a cell at a given res
- * @param end ending value on the space-filling curve for a cell at a given res
- * @param res spatial res represented as a bit shift (MSB is lower res)
- */
- private void relateAndRecurse(final long start, final long end, final short res) {
- final double minLon = GeoUtils.mortonUnhashLon(start);
- final double minLat = GeoUtils.mortonUnhashLat(start);
- final double maxLon = GeoUtils.mortonUnhashLon(end);
- final double maxLat = GeoUtils.mortonUnhashLat(end);
-
- final short level = (short)((GeoUtils.BITS<<1)-res>>>1);
-
- // if cell is within and a factor of the precision step, or it crosses the edge of the shape add the range
- final boolean within = res % GeoPointField.PRECISION_STEP == 0 && cellWithin(minLon, minLat, maxLon, maxLat);
- if (within || (level == DETAIL_LEVEL && cellIntersectsShape(minLon, minLat, maxLon, maxLat))) {
- final short nextRes = (short)(res-1);
- if (nextRes % GeoPointField.PRECISION_STEP == 0) {
- rangeBounds.add(new Range(start, nextRes, !within));
- rangeBounds.add(new Range(start|(1L<<nextRes), nextRes, !within));
- } else {
- rangeBounds.add(new Range(start, res, !within));
- }
- } else if (level < DETAIL_LEVEL && cellIntersectsMBR(minLon, minLat, maxLon, maxLat)) {
- computeRange(start, (short) (res - 1));
+ static GeoPointTermsEnum newInstance(final TermsEnum terms, final GeoPointMultiTermQuery query) {
+ if (query.termEncoding == TermEncoding.PREFIX) {
+ return new GeoPointPrefixTermsEnum(terms, query);
+ } else if (query.termEncoding == TermEncoding.NUMERIC) {
+ return new GeoPointNumericTermsEnum(terms, query);
}
- }
-
- protected short computeMaxShift() {
- // in this case a factor of 4 brings the detail level to ~0.002/0.001 degrees lon/lat respectively (or ~222m/111m)
- return GeoPointField.PRECISION_STEP * 4;
- }
-
- /**
- * Determine whether the quad-cell crosses the shape
- */
- protected abstract boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat);
-
- /**
- * Determine whether quad-cell is within the shape
- */
- protected abstract boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat);
-
- /**
- * Default shape is a rectangle, so this returns the same as {@code cellIntersectsMBR}
- */
- protected abstract boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat);
-
- /**
- * Primary driver for cells intersecting shape boundaries
- */
- protected boolean cellIntersectsMBR(final double minLon, final double minLat, final double maxLon, final double maxLat) {
- return GeoRelationUtils.rectIntersects(minLon, minLat, maxLon, maxLat, this.minLon, this.minLat, this.maxLon, this.maxLat);
- }
-
- /**
- * Return whether quad-cell contains the bounding box of this shape
- */
- protected boolean cellContains(final double minLon, final double minLat, final double maxLon, final double maxLat) {
- return GeoRelationUtils.rectWithin(this.minLon, this.minLat, this.maxLon, this.maxLat, minLon, minLat, maxLon, maxLat);
+ throw new IllegalArgumentException("Invalid GeoPoint TermEncoding " + query.termEncoding);
}
public boolean boundaryTerm() {
- if (currentRange == null) {
+ if (currentCell == null) {
throw new IllegalStateException("GeoPointTermsEnum empty or not initialized");
}
return currentRange.boundary;
}
- private void nextRange() {
- currentRange = rangeBounds.remove(0);
- currentRange.fillBytesRef(currentCell);
+ protected BytesRef peek() {
+ return nextSubRangeBRB.get();
}
- @Override
- protected final BytesRef nextSeekTerm(BytesRef term) {
- while (!rangeBounds.isEmpty()) {
- if (currentRange == null) {
- nextRange();
- }
-
- // if the new upper bound is before the term parameter, the sub-range is never a hit
- if (term != null && term.compareTo(currentCell.get()) > 0) {
- nextRange();
- if (!rangeBounds.isEmpty()) {
- continue;
- }
- }
- // never seek backwards, so use current term if lower bound is smaller
- return (term != null && term.compareTo(currentCell.get()) > 0) ?
- term : currentCell.get();
- }
+ abstract protected boolean hasNext();
- // no more sub-range enums available
- assert rangeBounds.isEmpty();
- return null;
+ protected void nextRange() {
+ currentRange.fillBytesRef(currentCellBRB);
+ currentCell = currentCellBRB.get();
}
/**
@@ -196,13 +83,12 @@ abstract class GeoPointTermsEnum extends FilteredTermsEnum {
@Override
protected AcceptStatus accept(BytesRef term) {
// validate value is in range
- while (currentCell == null || term.compareTo(currentCell.get()) > 0) {
- if (rangeBounds.isEmpty()) {
+ while (currentCell == null || term.compareTo(currentCell) > 0) {
+ if (hasNext() == false) {
return AcceptStatus.END;
}
// peek next sub-range, only seek if the current term is smaller than next lower bound
- rangeBounds.get(0).fillBytesRef(this.nextSubRange);
- if (term.compareTo(this.nextSubRange.get()) < 0) {
+ if (term.compareTo(peek()) < 0) {
return AcceptStatus.NO_AND_SEEK;
}
// step forward to next range without seeking, as next range is less or equal current term
@@ -212,17 +98,19 @@ abstract class GeoPointTermsEnum extends FilteredTermsEnum {
return AcceptStatus.YES;
}
- protected abstract boolean postFilter(final double lon, final double lat);
+ protected boolean postFilter(final double lon, final double lat) {
+ return relationImpl.postFilter(lon, lat);
+ }
/**
* Internal class to represent a range along the space filling curve
*/
- protected final class Range implements Comparable<Range> {
- final short shift;
- final long start;
- final boolean boundary;
+ abstract class BaseRange implements Comparable<BaseRange> {
+ protected short shift;
+ protected long start;
+ protected boolean boundary;
- Range(final long lower, final short shift, boolean boundary) {
+ BaseRange(final long lower, final short shift, boolean boundary) {
this.boundary = boundary;
this.start = lower;
this.shift = shift;
@@ -232,18 +120,21 @@ abstract class GeoPointTermsEnum extends FilteredTermsEnum {
* Encode as a BytesRef using a reusable object. This allows us to lazily create the BytesRef (which is
* quite expensive), only when we need it.
*/
- private void fillBytesRef(BytesRefBuilder result) {
- assert result != null;
- NumericUtils.longToPrefixCoded(start, shift, result);
- }
+ abstract protected void fillBytesRef(BytesRefBuilder result);
@Override
- public int compareTo(Range other) {
+ public int compareTo(BaseRange other) {
final int result = Short.compare(this.shift, other.shift);
if (result == 0) {
return Long.compare(this.start, other.start);
}
return result;
}
+
+ protected void set(BaseRange other) {
+ this.start = other.start;
+ this.shift = other.shift;
+ this.boundary = other.boundary;
+ }
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoEncodingUtils.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoEncodingUtils.java b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoEncodingUtils.java
new file mode 100644
index 0000000..de62318
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoEncodingUtils.java
@@ -0,0 +1,157 @@
+package org.apache.lucene.spatial.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.util.BitUtil;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+
+import static org.apache.lucene.spatial.util.GeoUtils.MIN_LON_INCL;
+import static org.apache.lucene.spatial.util.GeoUtils.MIN_LAT_INCL;
+
+/**
+ * Basic reusable geopoint encoding methods
+ *
+ * @lucene.experimental
+ */
+public final class GeoEncodingUtils {
+ /** number of bits used for quantizing latitude and longitude values */
+ public static final short BITS = 31;
+ private static final double LON_SCALE = (0x1L<<BITS)/360.0D;
+ private static final double LAT_SCALE = (0x1L<<BITS)/180.0D;
+
+ /**
+ * The maximum term length (used for <code>byte[]</code> buffer size)
+ * for encoding <code>geoEncoded</code> values.
+ * @see #geoCodedToPrefixCodedBytes(long, int, BytesRefBuilder)
+ */
+ public static final int BUF_SIZE_LONG = 28/8 + 1;
+
+ /** rounding error for quantized latitude and longitude values */
+ public static final double TOLERANCE = 1E-6;
+
+ // No instance:
+ private GeoEncodingUtils() {
+ }
+
+ public static final Long mortonHash(final double lon, final double lat) {
+ return BitUtil.interleave(scaleLon(lon), scaleLat(lat));
+ }
+
+ public static final double mortonUnhashLon(final long hash) {
+ return unscaleLon(BitUtil.deinterleave(hash));
+ }
+
+ public static final double mortonUnhashLat(final long hash) {
+ return unscaleLat(BitUtil.deinterleave(hash >>> 1));
+ }
+
+ protected static final long scaleLon(final double val) {
+ return (long) ((val-MIN_LON_INCL) * LON_SCALE);
+ }
+
+ protected static final long scaleLat(final double val) {
+ return (long) ((val-MIN_LAT_INCL) * LAT_SCALE);
+ }
+
+ protected static final double unscaleLon(final long val) {
+ return (val / LON_SCALE) + MIN_LON_INCL;
+ }
+
+ protected static final double unscaleLat(final long val) {
+ return (val / LAT_SCALE) + MIN_LAT_INCL;
+ }
+
+ /**
+ * Compare two position values within a {@link GeoEncodingUtils#TOLERANCE} factor
+ */
+ public static double compare(final double v1, final double v2) {
+ final double delta = v1-v2;
+ return Math.abs(delta) <= TOLERANCE ? 0 : delta;
+ }
+
+ /**
+ * Convert a geocoded morton long into a prefix coded geo term
+ */
+ public static void geoCodedToPrefixCoded(long hash, int shift, BytesRefBuilder bytes) {
+ geoCodedToPrefixCodedBytes(hash, shift, bytes);
+ }
+
+ /**
+ * Convert a prefix coded geo term back into the geocoded morton long
+ */
+ public static long prefixCodedToGeoCoded(final BytesRef val) {
+ final long result = fromBytes((byte)0, (byte)0, (byte)0, (byte)0,
+ val.bytes[val.offset+0], val.bytes[val.offset+1], val.bytes[val.offset+2], val.bytes[val.offset+3]);
+ return result << 32;
+ }
+
+ /**
+ * GeoTerms are coded using 4 prefix bytes + 1 byte to record number of prefix bits
+ *
+ * example prefix at shift 54 (yields 10 significant prefix bits):
+ * pppppppp pp000000 00000000 00000000 00001010
+ * (byte 1) (byte 2) (byte 3) (byte 4) (sigbits)
+ */
+ private static void geoCodedToPrefixCodedBytes(final long hash, final int shift, final BytesRefBuilder bytes) {
+ // ensure shift is 32..63
+ if (shift < 32 || shift > 63) {
+ throw new IllegalArgumentException("Illegal shift value, must be 32..63; got shift=" + shift);
+ }
+ int nChars = BUF_SIZE_LONG + 1; // one extra for the byte that contains the number of significant bits
+ bytes.setLength(nChars);
+ bytes.grow(nChars--);
+ final int sigBits = 64 - shift;
+ bytes.setByteAt(BUF_SIZE_LONG, (byte)(sigBits));
+ long sortableBits = hash;
+ sortableBits >>>= shift;
+ sortableBits <<= 32 - sigBits;
+ do {
+ bytes.setByteAt(--nChars, (byte)(sortableBits));
+ sortableBits >>>= 8;
+ } while (nChars > 0);
+ }
+
+ /** Get the prefix coded geo term shift value */
+ public static int getPrefixCodedShift(final BytesRef val) {
+ final int shift = val.bytes[val.offset + BUF_SIZE_LONG];
+ if (shift > 63 || shift < 0)
+ throw new NumberFormatException("Invalid shift value (" + shift + ") in prefixCoded bytes (is encoded value really a geo point?)");
+ return shift;
+ }
+
+ /** Converts 8 bytes to a long value */
+ protected static long fromBytes(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7, byte b8) {
+ return ((long)b1 & 255L) << 56 | ((long)b2 & 255L) << 48 | ((long)b3 & 255L) << 40
+ | ((long)b4 & 255L) << 32 | ((long)b5 & 255L) << 24 | ((long)b6 & 255L) << 16
+ | ((long)b7 & 255L) << 8 | (long)b8 & 255L;
+ }
+
+ /** Converts a long value into a bit string (useful for debugging) */
+ public static String geoTermToString(long term) {
+ StringBuilder s = new StringBuilder(64);
+ final int numberOfLeadingZeros = Long.numberOfLeadingZeros(term);
+ for (int i = 0; i < numberOfLeadingZeros; i++) {
+ s.append('0');
+ }
+ if (term != 0) {
+ s.append(Long.toBinaryString(term));
+ }
+ return s.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoHashUtils.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoHashUtils.java b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoHashUtils.java
index 9450c1e..49a7571 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoHashUtils.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoHashUtils.java
@@ -40,7 +40,7 @@ public class GeoHashUtils {
/** maximum precision for geohash strings */
public static final int PRECISION = 12;
- private static final short MORTON_OFFSET = (GeoUtils.BITS<<1) - (PRECISION*5);
+ private static final short MORTON_OFFSET = (GeoEncodingUtils.BITS<<1) - (PRECISION*5);
// No instance:
private GeoHashUtils() {
@@ -52,7 +52,7 @@ public class GeoHashUtils {
public static final long longEncode(final double lon, final double lat, final int level) {
// shift to appropriate level
final short msf = (short)(((12 - level) * 5) + MORTON_OFFSET);
- return ((BitUtil.flipFlop(GeoUtils.mortonHash(lon, lat)) >>> msf) << 4) | level;
+ return ((BitUtil.flipFlop(GeoEncodingUtils.mortonHash(lon, lat)) >>> msf) << 4) | level;
}
/**
@@ -118,7 +118,7 @@ public class GeoHashUtils {
*/
public static final String stringEncode(final double lon, final double lat, final int level) {
// convert to geohashlong
- final long ghLong = fromMorton(GeoUtils.mortonHash(lon, lat), level);
+ final long ghLong = fromMorton(GeoEncodingUtils.mortonHash(lon, lat), level);
return stringEncode(ghLong);
}
@@ -139,7 +139,7 @@ public class GeoHashUtils {
StringBuilder geoHash = new StringBuilder();
short precision = 0;
- final short msf = (GeoUtils.BITS<<1)-5;
+ final short msf = (GeoEncodingUtils.BITS<<1)-5;
long mask = 31L<<msf;
do {
geoHash.append(BASE_32[(int)((mask & hashedVal)>>>(msf-(precision*5)))]);
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoRelationUtils.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoRelationUtils.java b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoRelationUtils.java
index 1911720..32e30bd 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoRelationUtils.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoRelationUtils.java
@@ -174,14 +174,14 @@ public class GeoRelationUtils {
c2 = a2*shapeX[p+1] + b2*shapeY[p+1];
s = (1/d)*(b2*c1 - b1*c2);
t = (1/d)*(a1*c2 - a2*c1);
- x00 = StrictMath.min(bbox[b][0], bbox[b+1][0]) - GeoUtils.TOLERANCE;
- x01 = StrictMath.max(bbox[b][0], bbox[b+1][0]) + GeoUtils.TOLERANCE;
- y00 = StrictMath.min(bbox[b][1], bbox[b+1][1]) - GeoUtils.TOLERANCE;
- y01 = StrictMath.max(bbox[b][1], bbox[b+1][1]) + GeoUtils.TOLERANCE;
- x10 = StrictMath.min(shapeX[p], shapeX[p+1]) - GeoUtils.TOLERANCE;
- x11 = StrictMath.max(shapeX[p], shapeX[p+1]) + GeoUtils.TOLERANCE;
- y10 = StrictMath.min(shapeY[p], shapeY[p+1]) - GeoUtils.TOLERANCE;
- y11 = StrictMath.max(shapeY[p], shapeY[p+1]) + GeoUtils.TOLERANCE;
+ x00 = StrictMath.min(bbox[b][0], bbox[b+1][0]) - GeoEncodingUtils.TOLERANCE;
+ x01 = StrictMath.max(bbox[b][0], bbox[b+1][0]) + GeoEncodingUtils.TOLERANCE;
+ y00 = StrictMath.min(bbox[b][1], bbox[b+1][1]) - GeoEncodingUtils.TOLERANCE;
+ y01 = StrictMath.max(bbox[b][1], bbox[b+1][1]) + GeoEncodingUtils.TOLERANCE;
+ x10 = StrictMath.min(shapeX[p], shapeX[p+1]) - GeoEncodingUtils.TOLERANCE;
+ x11 = StrictMath.max(shapeX[p], shapeX[p+1]) + GeoEncodingUtils.TOLERANCE;
+ y10 = StrictMath.min(shapeY[p], shapeY[p+1]) - GeoEncodingUtils.TOLERANCE;
+ y11 = StrictMath.max(shapeY[p], shapeY[p+1]) + GeoEncodingUtils.TOLERANCE;
// check whether the intersection point is touching one of the line segments
boolean touching = ((x00 == s && y00 == t) || (x01 == s && y01 == t))
|| ((x10 == s && y10 == t) || (x11 == s && y11 == t));
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoUtils.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoUtils.java b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoUtils.java
index 8899170..5becef7 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoUtils.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoUtils.java
@@ -18,8 +18,6 @@ package org.apache.lucene.spatial.util;
import java.util.ArrayList;
-import org.apache.lucene.util.BitUtil;
-
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Math.PI;
@@ -30,6 +28,7 @@ import static org.apache.lucene.util.SloppyMath.cos;
import static org.apache.lucene.util.SloppyMath.sin;
import static org.apache.lucene.util.SloppyMath.TO_DEGREES;
import static org.apache.lucene.util.SloppyMath.TO_RADIANS;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.TOLERANCE;
import static org.apache.lucene.spatial.util.GeoProjectionUtils.MAX_LAT_RADIANS;
import static org.apache.lucene.spatial.util.GeoProjectionUtils.MAX_LON_RADIANS;
import static org.apache.lucene.spatial.util.GeoProjectionUtils.MIN_LAT_RADIANS;
@@ -43,13 +42,6 @@ import static org.apache.lucene.spatial.util.GeoProjectionUtils.SEMIMAJOR_AXIS;
* @lucene.experimental
*/
public final class GeoUtils {
- /** number of bits used for quantizing latitude and longitude values */
- public static final short BITS = 31;
- private static final double LON_SCALE = (0x1L<<BITS)/360.0D;
- private static final double LAT_SCALE = (0x1L<<BITS)/180.0D;
- /** rounding error for quantized latitude and longitude values */
- public static final double TOLERANCE = 1E-6;
-
/** Minimum longitude value. */
public static final double MIN_LON_INCL = -180.0D;
@@ -66,44 +58,14 @@ public final class GeoUtils {
private GeoUtils() {
}
- /**
- * encode longitude, latitude geopoint values using morton encoding method
- * https://en.wikipedia.org/wiki/Z-order_curve
- */
- public static final Long mortonHash(final double lon, final double lat) {
- return BitUtil.interleave(scaleLon(lon), scaleLat(lat));
- }
-
- /** decode longitude value from morton encoded geo point */
- public static final double mortonUnhashLon(final long hash) {
- return unscaleLon(BitUtil.deinterleave(hash));
- }
-
- /** decode latitude value from morton encoded geo point */
- public static final double mortonUnhashLat(final long hash) {
- return unscaleLat(BitUtil.deinterleave(hash >>> 1));
- }
-
- private static final long scaleLon(final double val) {
- return (long) ((val-MIN_LON_INCL) * LON_SCALE);
- }
-
- private static final long scaleLat(final double val) {
- return (long) ((val-MIN_LAT_INCL) * LAT_SCALE);
- }
-
- private static final double unscaleLon(final long val) {
- return (val / LON_SCALE) + MIN_LON_INCL;
- }
-
- private static final double unscaleLat(final long val) {
- return (val / LAT_SCALE) + MIN_LAT_INCL;
+ /** validates latitude value is within standard +/-90 coordinate bounds */
+ public static boolean isValidLat(double lat) {
+ return Double.isNaN(lat) == false && lat >= MIN_LAT_INCL && lat <= MAX_LAT_INCL;
}
- /** Compare two position values within a {@link GeoUtils#TOLERANCE} factor */
- public static double compare(final double v1, final double v2) {
- final double delta = v1-v2;
- return abs(delta) <= TOLERANCE ? 0 : delta;
+ /** validates longitude value is within standard +/-180 coordinate bounds */
+ public static boolean isValidLon(double lon) {
+ return Double.isNaN(lon) == false && lon >= MIN_LON_INCL && lon <= MAX_LON_INCL;
}
/** Puts longitude in range of -180 to +180. */
@@ -130,28 +92,15 @@ public final class GeoUtils {
return (off <= 180 ? off : 360-off) - 90;
}
- /** Converts long value to bit string (useful for debugging) */
- public static String geoTermToString(long term) {
- StringBuilder s = new StringBuilder(64);
- final int numberOfLeadingZeros = Long.numberOfLeadingZeros(term);
- for (int i = 0; i < numberOfLeadingZeros; i++) {
- s.append('0');
- }
- if (term != 0) {
- s.append(Long.toBinaryString(term));
- }
- return s.toString();
- }
-
/**
* Converts a given circle (defined as a point/radius) to an approximated line-segment polygon
*
- * @param lon longitudinal center of circle (in degrees)
- * @param lat latitudinal center of circle (in degrees)
+ * @param lon longitudinal center of circle (in degrees)
+ * @param lat latitudinal center of circle (in degrees)
* @param radiusMeters distance radius of circle (in meters)
* @return a list of lon/lat points representing the circle
*/
- @SuppressWarnings({"unchecked","rawtypes"})
+ @SuppressWarnings({"unchecked", "rawtypes"})
public static ArrayList<double[]> circleToPoly(final double lon, final double lat, final double radiusMeters) {
double angle;
// a little under-sampling (to limit the number of polygonal points): using archimedes estimation of pi
@@ -161,9 +110,9 @@ public final class GeoUtils {
double[] lats = new double[sides];
double[] pt = new double[2];
- final int sidesLen = sides-1;
- for (int i=0; i<sidesLen; ++i) {
- angle = (i*360/sides);
+ final int sidesLen = sides - 1;
+ for (int i = 0; i < sidesLen; ++i) {
+ angle = (i * 360 / sides);
pt = pointFromLonLatBearingGreatCircle(lon, lat, angle, radiusMeters, pt);
lons[i] = pt[0];
lats[i] = pt[1];
@@ -235,14 +184,4 @@ public final class GeoUtils {
return new GeoRect(max(minLon - TOLERANCE, MIN_LON_INCL), min(maxLon + TOLERANCE, MAX_LON_INCL),
max(minLat - TOLERANCE, MIN_LAT_INCL), min(maxLat + TOLERANCE, MAX_LAT_INCL));
}
-
- /** validates latitude value is within standard +/-90 coordinate bounds */
- public static boolean isValidLat(double lat) {
- return Double.isNaN(lat) == false && lat >= MIN_LAT_INCL && lat <= MAX_LAT_INCL;
- }
-
- /** validates longitude value is within standard +/-180 coordinate bounds */
- public static boolean isValidLon(double lon) {
- return Double.isNaN(lon) == false && lon >= MIN_LON_INCL && lon <= MAX_LON_INCL;
- }
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/test/org/apache/lucene/spatial/search/TestGeoPointQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/search/TestGeoPointQuery.java b/lucene/spatial/src/test/org/apache/lucene/spatial/search/TestGeoPointQuery.java
index cfd51af..083b28a 100644
--- a/lucene/spatial/src/test/org/apache/lucene/spatial/search/TestGeoPointQuery.java
+++ b/lucene/spatial/src/test/org/apache/lucene/spatial/search/TestGeoPointQuery.java
@@ -27,7 +27,9 @@ import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
import org.apache.lucene.spatial.document.GeoPointField;
+import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
import org.apache.lucene.spatial.util.BaseGeoPointTestCase;
import org.apache.lucene.spatial.util.GeoRect;
import org.apache.lucene.spatial.util.GeoRelationUtils;
@@ -50,61 +52,63 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
private static Directory directory = null;
private static IndexReader reader = null;
private static IndexSearcher searcher = null;
+ private static TermEncoding termEncoding = null;
+ private static FieldType fieldType = null;
@Override
protected void addPointToDoc(String field, Document doc, double lat, double lon) {
- doc.add(new GeoPointField(field, lon, lat, Field.Store.NO));
+ doc.add(new GeoPointField(field, lon, lat, fieldType));
}
@Override
protected Query newBBoxQuery(String field, GeoRect rect) {
- return new GeoPointInBBoxQuery(field, rect.minLon, rect.minLat, rect.maxLon, rect.maxLat);
+ return new GeoPointInBBoxQuery(field, termEncoding, rect.minLon, rect.minLat, rect.maxLon, rect.maxLat);
}
@Override
protected Query newDistanceQuery(String field, double centerLat, double centerLon, double radiusMeters) {
- return new GeoPointDistanceQuery(field, centerLon, centerLat, radiusMeters);
+ return new GeoPointDistanceQuery(field, termEncoding, centerLon, centerLat, radiusMeters);
}
@Override
protected Query newDistanceRangeQuery(String field, double centerLat, double centerLon, double minRadiusMeters, double radiusMeters) {
- return new GeoPointDistanceRangeQuery(field, centerLon, centerLat, minRadiusMeters, radiusMeters);
+ return new GeoPointDistanceRangeQuery(field, termEncoding, centerLon, centerLat, minRadiusMeters, radiusMeters);
}
@Override
protected Query newPolygonQuery(String field, double[] lats, double[] lons) {
- return new GeoPointInPolygonQuery(field, lons, lats);
+ return new GeoPointInPolygonQuery(field, termEncoding, lons, lats);
}
@BeforeClass
public static void beforeClass() throws Exception {
directory = newDirectory();
+ termEncoding = TermEncoding.PREFIX;// randomTermEncoding();
+ fieldType = randomFieldType();
RandomIndexWriter writer = new RandomIndexWriter(random(), directory,
newIndexWriterConfig(new MockAnalyzer(random()))
.setMaxBufferedDocs(TestUtil.nextInt(random(), 100, 1000))
.setMergePolicy(newLogMergePolicy()));
- // create some simple geo points
- final FieldType storedPoint = new FieldType(GeoPointField.TYPE_STORED);
// this is a simple systematic test
GeoPointField[] pts = new GeoPointField[] {
- new GeoPointField(FIELD_NAME, -96.774, 32.763420, storedPoint),
- new GeoPointField(FIELD_NAME, -96.7759895324707, 32.7559529921407, storedPoint),
- new GeoPointField(FIELD_NAME, -96.77701950073242, 32.77866942010977, storedPoint),
- new GeoPointField(FIELD_NAME, -96.7706036567688, 32.7756745755423, storedPoint),
- new GeoPointField(FIELD_NAME, -139.73458170890808, 27.703618681345585, storedPoint),
- new GeoPointField(FIELD_NAME, -96.4538113027811, 32.94823588839368, storedPoint),
- new GeoPointField(FIELD_NAME, -96.65084838867188, 33.06047141970814, storedPoint),
- new GeoPointField(FIELD_NAME, -96.7772, 32.778650, storedPoint),
- new GeoPointField(FIELD_NAME, -177.23537676036358, -88.56029371730983, storedPoint),
- new GeoPointField(FIELD_NAME, -26.779373834241003, 33.541429799076354, storedPoint),
- new GeoPointField(FIELD_NAME, -77.35379276106497, 26.774024500421728, storedPoint),
- new GeoPointField(FIELD_NAME, -14.796283808944777, -90.0, storedPoint),
- new GeoPointField(FIELD_NAME, -178.8538113027811, 32.94823588839368, storedPoint),
- new GeoPointField(FIELD_NAME, 178.8538113027811, 32.94823588839368, storedPoint),
- new GeoPointField(FIELD_NAME, -73.998776, 40.720611, storedPoint),
- new GeoPointField(FIELD_NAME, -179.5, -44.5, storedPoint)};
+ new GeoPointField(FIELD_NAME, -96.774, 32.763420, fieldType),
+ new GeoPointField(FIELD_NAME, -96.7759895324707, 32.7559529921407, fieldType),
+ new GeoPointField(FIELD_NAME, -96.77701950073242, 32.77866942010977, fieldType),
+ new GeoPointField(FIELD_NAME, -96.7706036567688, 32.7756745755423, fieldType),
+ new GeoPointField(FIELD_NAME, -139.73458170890808, 27.703618681345585, fieldType),
+ new GeoPointField(FIELD_NAME, -96.4538113027811, 32.94823588839368, fieldType),
+ new GeoPointField(FIELD_NAME, -96.65084838867188, 33.06047141970814, fieldType),
+ new GeoPointField(FIELD_NAME, -96.7772, 32.778650, fieldType),
+ new GeoPointField(FIELD_NAME, -177.23537676036358, -88.56029371730983, fieldType),
+ new GeoPointField(FIELD_NAME, -26.779373834241003, 33.541429799076354, fieldType),
+ new GeoPointField(FIELD_NAME, -77.35379276106497, 26.774024500421728, fieldType),
+ new GeoPointField(FIELD_NAME, -14.796283808944777, -90.0, fieldType),
+ new GeoPointField(FIELD_NAME, -178.8538113027811, 32.94823588839368, fieldType),
+ new GeoPointField(FIELD_NAME, 178.8538113027811, 32.94823588839368, fieldType),
+ new GeoPointField(FIELD_NAME, -73.998776, 40.720611, fieldType),
+ new GeoPointField(FIELD_NAME, -179.5, -44.5, fieldType)};
for (GeoPointField p : pts) {
Document doc = new Document();
@@ -141,27 +145,38 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
directory = null;
}
+ private static TermEncoding randomTermEncoding() {
+ return random().nextBoolean() ? TermEncoding.NUMERIC : TermEncoding.PREFIX;
+ }
+
+ private static FieldType randomFieldType() {
+ if (termEncoding == TermEncoding.PREFIX) {
+ return GeoPointField.PREFIX_TYPE_NOT_STORED;
+ }
+ return GeoPointField.NUMERIC_TYPE_NOT_STORED;
+ }
+
private TopDocs bboxQuery(double minLon, double minLat, double maxLon, double maxLat, int limit) throws Exception {
- GeoPointInBBoxQuery q = new GeoPointInBBoxQuery(FIELD_NAME, minLon, minLat, maxLon, maxLat);
+ GeoPointInBBoxQuery q = new GeoPointInBBoxQuery(FIELD_NAME, termEncoding, minLon, minLat, maxLon, maxLat);
return searcher.search(q, limit);
}
private TopDocs polygonQuery(double[] lon, double[] lat, int limit) throws Exception {
- GeoPointInPolygonQuery q = new GeoPointInPolygonQuery(FIELD_NAME, lon, lat);
+ GeoPointInPolygonQuery q = new GeoPointInPolygonQuery(FIELD_NAME, termEncoding, lon, lat);
return searcher.search(q, limit);
}
private TopDocs geoDistanceQuery(double lon, double lat, double radius, int limit) throws Exception {
- GeoPointDistanceQuery q = new GeoPointDistanceQuery(FIELD_NAME, lon, lat, radius);
+ GeoPointDistanceQuery q = new GeoPointDistanceQuery(FIELD_NAME, termEncoding, lon, lat, radius);
return searcher.search(q, limit);
}
@Override
protected Boolean rectContainsPoint(GeoRect rect, double pointLat, double pointLon) {
- if (GeoUtils.compare(pointLon, rect.minLon) == 0.0 ||
- GeoUtils.compare(pointLon, rect.maxLon) == 0.0 ||
- GeoUtils.compare(pointLat, rect.minLat) == 0.0 ||
- GeoUtils.compare(pointLat, rect.maxLat) == 0.0) {
+ if (GeoEncodingUtils.compare(pointLon, rect.minLon) == 0.0 ||
+ GeoEncodingUtils.compare(pointLon, rect.maxLon) == 0.0 ||
+ GeoEncodingUtils.compare(pointLat, rect.minLat) == 0.0 ||
+ GeoEncodingUtils.compare(pointLat, rect.maxLat) == 0.0) {
// Point is very close to rect boundary
return null;
}
@@ -202,12 +217,12 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
private static boolean radiusQueryCanBeWrong(double centerLat, double centerLon, double ptLon, double ptLat,
final double radius) {
- final long hashedCntr = GeoUtils.mortonHash(centerLon, centerLat);
- centerLon = GeoUtils.mortonUnhashLon(hashedCntr);
- centerLat = GeoUtils.mortonUnhashLat(hashedCntr);
- final long hashedPt = GeoUtils.mortonHash(ptLon, ptLat);
- ptLon = GeoUtils.mortonUnhashLon(hashedPt);
- ptLat = GeoUtils.mortonUnhashLat(hashedPt);
+ final long hashedCntr = GeoEncodingUtils.mortonHash(centerLon, centerLat);
+ centerLon = GeoEncodingUtils.mortonUnhashLon(hashedCntr);
+ centerLat = GeoEncodingUtils.mortonUnhashLat(hashedCntr);
+ final long hashedPt = GeoEncodingUtils.mortonHash(ptLon, ptLat);
+ ptLon = GeoEncodingUtils.mortonUnhashLon(hashedPt);
+ ptLat = GeoEncodingUtils.mortonUnhashLat(hashedPt);
double ptDistance = SloppyMath.haversin(centerLat, centerLon, ptLat, ptLon)*1000.0;
double delta = StrictMath.abs(ptDistance - radius);
@@ -222,7 +237,7 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
private TopDocs geoDistanceRangeQuery(double lon, double lat, double minRadius, double maxRadius, int limit)
throws Exception {
- GeoPointDistanceRangeQuery q = new GeoPointDistanceRangeQuery(FIELD_NAME, lon, lat, minRadius, maxRadius);
+ GeoPointDistanceRangeQuery q = new GeoPointDistanceRangeQuery(FIELD_NAME, termEncoding, lon, lat, minRadius, maxRadius);
return searcher.search(q, limit);
}
@@ -342,9 +357,9 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
}
public void testMortonEncoding() throws Exception {
- long hash = GeoUtils.mortonHash(180, 90);
- assertEquals(180.0, GeoUtils.mortonUnhashLon(hash), 0);
- assertEquals(90.0, GeoUtils.mortonUnhashLat(hash), 0);
+ long hash = GeoEncodingUtils.mortonHash(180, 90);
+ assertEquals(180.0, GeoEncodingUtils.mortonUnhashLon(hash), 0);
+ assertEquals(90.0, GeoEncodingUtils.mortonUnhashLat(hash), 0);
}
public void testEncodeDecode() throws Exception {
@@ -354,12 +369,12 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
double lat = randomLat(small);
double lon = randomLon(small);
- long enc = GeoUtils.mortonHash(lon, lat);
- double latEnc = GeoUtils.mortonUnhashLat(enc);
- double lonEnc = GeoUtils.mortonUnhashLon(enc);
+ long enc = GeoEncodingUtils.mortonHash(lon, lat);
+ double latEnc = GeoEncodingUtils.mortonUnhashLat(enc);
+ double lonEnc = GeoEncodingUtils.mortonUnhashLon(enc);
- assertEquals("lat=" + lat + " latEnc=" + latEnc + " diff=" + (lat - latEnc), lat, latEnc, GeoUtils.TOLERANCE);
- assertEquals("lon=" + lon + " lonEnc=" + lonEnc + " diff=" + (lon - lonEnc), lon, lonEnc, GeoUtils.TOLERANCE);
+ assertEquals("lat=" + lat + " latEnc=" + latEnc + " diff=" + (lat - latEnc), lat, latEnc, GeoEncodingUtils.TOLERANCE);
+ assertEquals("lon=" + lon + " lonEnc=" + lonEnc + " diff=" + (lon - lonEnc), lon, lonEnc, GeoEncodingUtils.TOLERANCE);
}
}
@@ -370,13 +385,13 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
double lat = randomLat(small);
double lon = randomLon(small);
- long enc = GeoUtils.mortonHash(lon, lat);
- double latEnc = GeoUtils.mortonUnhashLat(enc);
- double lonEnc = GeoUtils.mortonUnhashLon(enc);
+ long enc = GeoEncodingUtils.mortonHash(lon, lat);
+ double latEnc = GeoEncodingUtils.mortonUnhashLat(enc);
+ double lonEnc = GeoEncodingUtils.mortonUnhashLon(enc);
- long enc2 = GeoUtils.mortonHash(lon, lat);
- double latEnc2 = GeoUtils.mortonUnhashLat(enc2);
- double lonEnc2 = GeoUtils.mortonUnhashLon(enc2);
+ long enc2 = GeoEncodingUtils.mortonHash(lon, lat);
+ double latEnc2 = GeoEncodingUtils.mortonUnhashLat(enc2);
+ double lonEnc2 = GeoEncodingUtils.mortonUnhashLon(enc2);
assertEquals(latEnc, latEnc2, 0.0);
assertEquals(lonEnc, lonEnc2, 0.0);
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/test/org/apache/lucene/spatial/util/BaseGeoPointTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/util/BaseGeoPointTestCase.java b/lucene/spatial/src/test/org/apache/lucene/spatial/util/BaseGeoPointTestCase.java
index cf26b88..2691ff8 100644
--- a/lucene/spatial/src/test/org/apache/lucene/spatial/util/BaseGeoPointTestCase.java
+++ b/lucene/spatial/src/test/org/apache/lucene/spatial/util/BaseGeoPointTestCase.java
@@ -57,8 +57,8 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
protected static final String FIELD_NAME = "point";
- private static final double LON_SCALE = (0x1L<<GeoUtils.BITS)/360.0D;
- private static final double LAT_SCALE = (0x1L<<GeoUtils.BITS)/180.0D;
+ private static final double LON_SCALE = (0x1L<< GeoEncodingUtils.BITS)/360.0D;
+ private static final double LAT_SCALE = (0x1L<< GeoEncodingUtils.BITS)/180.0D;
private static double originLat;
private static double originLon;
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/test/org/apache/lucene/spatial/util/TestGeoUtils.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/util/TestGeoUtils.java b/lucene/spatial/src/test/org/apache/lucene/spatial/util/TestGeoUtils.java
index c5480c7..8f7a391 100644
--- a/lucene/spatial/src/test/org/apache/lucene/spatial/util/TestGeoUtils.java
+++ b/lucene/spatial/src/test/org/apache/lucene/spatial/util/TestGeoUtils.java
@@ -24,6 +24,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.LuceneTestCase;
import org.junit.BeforeClass;
@@ -38,8 +39,8 @@ import static org.apache.lucene.spatial.util.GeoDistanceUtils.DISTANCE_PCT_ERR;
*/
public class TestGeoUtils extends LuceneTestCase {
- private static final double LON_SCALE = (0x1L<<GeoUtils.BITS)/360.0D;
- private static final double LAT_SCALE = (0x1L<<GeoUtils.BITS)/180.0D;
+ private static final double LON_SCALE = (0x1L<<GeoEncodingUtils.BITS)/360.0D;
+ private static final double LAT_SCALE = (0x1L<<GeoEncodingUtils.BITS)/180.0D;
// Global bounding box we will "cover" in the random test; we have to make this "smallish" else the queries take very long:
private static double originLat;
@@ -77,7 +78,7 @@ public class TestGeoUtils extends LuceneTestCase {
// compute geohash straight from lat/lon and from morton encoded value to ensure they're the same
randomGeoHashString = GeoHashUtils.stringEncode(lon, lat, randomLevel = random().nextInt(12 - 1) + 1);
- mortonGeoHash = GeoHashUtils.stringEncodeFromMortonLong(GeoUtils.mortonHash(lon, lat), randomLevel);
+ mortonGeoHash = GeoHashUtils.stringEncodeFromMortonLong(GeoEncodingUtils.mortonHash(lon, lat), randomLevel);
assertEquals(randomGeoHashString, mortonGeoHash);
// v&v conversion from lat/lon or geohashstring to geohash long and back to geohash string
@@ -90,8 +91,8 @@ public class TestGeoUtils extends LuceneTestCase {
assertEquals(mortonLongFromGHLong, mortonLongFromGHString);
// v&v lat/lon from geohash string and geohash long
- assertEquals(GeoUtils.mortonUnhashLat(mortonLongFromGHString), GeoUtils.mortonUnhashLat(mortonLongFromGHLong), 0);
- assertEquals(GeoUtils.mortonUnhashLon(mortonLongFromGHString), GeoUtils.mortonUnhashLon(mortonLongFromGHLong), 0);
+ assertEquals(GeoEncodingUtils.mortonUnhashLat(mortonLongFromGHString), GeoEncodingUtils.mortonUnhashLat(mortonLongFromGHLong), 0);
+ assertEquals(GeoEncodingUtils.mortonUnhashLon(mortonLongFromGHString), GeoEncodingUtils.mortonUnhashLon(mortonLongFromGHLong), 0);
}
}
@@ -115,8 +116,8 @@ public class TestGeoUtils extends LuceneTestCase {
final String geohash = GeoHashUtils.stringEncode(4.8909347, 52.3738007);
final long hash = GeoHashUtils.mortonEncode(geohash);
- assertEquals(52.3738007, GeoUtils.mortonUnhashLat(hash), 0.00001D);
- assertEquals(4.8909347, GeoUtils.mortonUnhashLon(hash), 0.00001D);
+ assertEquals(52.3738007, GeoEncodingUtils.mortonUnhashLat(hash), 0.00001D);
+ assertEquals(4.8909347, GeoEncodingUtils.mortonUnhashLon(hash), 0.00001D);
}
/**
@@ -128,18 +129,18 @@ public class TestGeoUtils extends LuceneTestCase {
final long hash = GeoHashUtils.mortonEncode(geohash);
- assertEquals(84.6, GeoUtils.mortonUnhashLat(hash), 0.00001D);
- assertEquals(10.5, GeoUtils.mortonUnhashLon(hash), 0.00001D);
+ assertEquals(84.6, GeoEncodingUtils.mortonUnhashLat(hash), 0.00001D);
+ assertEquals(10.5, GeoEncodingUtils.mortonUnhashLon(hash), 0.00001D);
}
public void testDecodeEncode() {
final String geoHash = "u173zq37x014";
assertEquals(geoHash, GeoHashUtils.stringEncode(4.8909347, 52.3738007));
final long mortonHash = GeoHashUtils.mortonEncode(geoHash);
- final double lon = GeoUtils.mortonUnhashLon(mortonHash);
- final double lat = GeoUtils.mortonUnhashLat(mortonHash);
- assertEquals(52.37380061d, GeoUtils.mortonUnhashLat(mortonHash), 0.000001d);
- assertEquals(4.8909343d, GeoUtils.mortonUnhashLon(mortonHash), 0.000001d);
+ final double lon = GeoEncodingUtils.mortonUnhashLon(mortonHash);
+ final double lat = GeoEncodingUtils.mortonUnhashLat(mortonHash);
+ assertEquals(52.37380061d, GeoEncodingUtils.mortonUnhashLat(mortonHash), 0.000001d);
+ assertEquals(4.8909343d, GeoEncodingUtils.mortonUnhashLon(mortonHash), 0.000001d);
assertEquals(geoHash, GeoHashUtils.stringEncode(lon, lat));
}
@@ -416,7 +417,7 @@ public class TestGeoUtils extends LuceneTestCase {
}
}
- /** Tests consistency of GeoUtils.rectWithinCircle, .rectCrossesCircle, .rectWithin and SloppyMath.haversine distance check */
+ /** Tests consistency of GeoEncodingUtils.rectWithinCircle, .rectCrossesCircle, .rectWithin and SloppyMath.haversine distance check */
public void testGeoRelations() throws Exception {
int numDocs = atLeast(1000);
@@ -543,4 +544,22 @@ public class TestGeoUtils extends LuceneTestCase {
}
}
}
+
+ /**
+ * Tests stability of {@link GeoEncodingUtils#geoCodedToPrefixCoded}
+ */
+ public void testGeoPrefixCoding() throws Exception {
+ int numIters = atLeast(1000);
+ long hash;
+ long decodedHash;
+ BytesRefBuilder brb = new BytesRefBuilder();
+ while (numIters-- >= 0) {
+ hash = GeoEncodingUtils.mortonHash(randomLon(false), randomLat(false));
+ for (int i=32; i<64; ++i) {
+ GeoEncodingUtils.geoCodedToPrefixCoded(hash, i, brb);
+ decodedHash = GeoEncodingUtils.prefixCodedToGeoCoded(brb.get());
+ assertEquals((hash >>> i) << i, decodedHash);
+ }
+ }
+ }
}
[4/5] lucene-solr git commit: refactor GeoPointInPolygonQuery to
rewrite using package private delegate
Posted by nk...@apache.org.
refactor GeoPointInPolygonQuery to rewrite using package private delegate
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/331c03dd
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/331c03dd
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/331c03dd
Branch: refs/heads/branch_5x
Commit: 331c03dde77a911c1a43b403caec00a2a1c53f38
Parents: f63f152
Author: nknize <nk...@apache.org>
Authored: Sat Feb 6 00:50:51 2016 -0600
Committer: nknize <nk...@apache.org>
Committed: Sat Feb 6 22:10:05 2016 -0600
----------------------------------------------------------------------
.../spatial/search/GeoPointInPolygonQuery.java | 58 ++---------
.../search/GeoPointInPolygonQueryImpl.java | 103 +++++++++++++++++++
2 files changed, 110 insertions(+), 51 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/331c03dd/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
index a3cc061..eb8c50c 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
@@ -18,10 +18,11 @@ package org.apache.lucene.spatial.search;
import java.util.Arrays;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
import org.apache.lucene.spatial.util.GeoEncodingUtils;
import org.apache.lucene.spatial.util.GeoRect;
-import org.apache.lucene.spatial.util.GeoRelationUtils;
import org.apache.lucene.spatial.util.GeoUtils;
/** Implements a simple point in polygon query on a GeoPoint field. This is based on
@@ -43,11 +44,11 @@ import org.apache.lucene.spatial.util.GeoUtils;
*
* @lucene.experimental
*/
-public final class GeoPointInPolygonQuery extends GeoPointInBBoxQueryImpl {
+public final class GeoPointInPolygonQuery extends GeoPointInBBoxQuery {
// polygon position arrays - this avoids the use of any objects or
// or geo library dependencies
- private final double[] x;
- private final double[] y;
+ protected final double[] x;
+ protected final double[] y;
public GeoPointInPolygonQuery(final String field, final double[] polyLons, final double[] polyLats) {
this(field, TermEncoding.PREFIX, GeoUtils.polyToBBox(polyLons, polyLats), polyLons, polyLats);
@@ -83,53 +84,8 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQueryImpl {
/** throw exception if trying to change rewrite method */
@Override
- public void setRewriteMethod(RewriteMethod method) {
- throw new UnsupportedOperationException("cannot change rewrite method");
- }
-
- @Override
- protected CellComparator newCellComparator() {
- return new GeoPolygonCellComparator(this);
- }
-
- /**
- * Custom {@code org.apache.lucene.spatial.search.GeoPointMultiTermQuery.CellComparator} that computes morton hash
- * ranges based on the defined edges of the provided polygon.
- */
- private final class GeoPolygonCellComparator extends CellComparator {
- GeoPolygonCellComparator(GeoPointMultiTermQuery query) {
- super(query);
- }
-
- @Override
- protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat) {
- return GeoRelationUtils.rectCrossesPolyApprox(minLon, minLat, maxLon, maxLat, x, y, GeoPointInPolygonQuery.this.minLon,
- GeoPointInPolygonQuery.this.minLat, GeoPointInPolygonQuery.this.maxLon, GeoPointInPolygonQuery.this.maxLat);
- }
-
- @Override
- protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat) {
- return GeoRelationUtils.rectWithinPolyApprox(minLon, minLat, maxLon, maxLat, x, y, GeoPointInPolygonQuery.this.minLon,
- GeoPointInPolygonQuery.this.minLat, GeoPointInPolygonQuery.this.maxLon, GeoPointInPolygonQuery.this.maxLat);
- }
-
- @Override
- protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat) {
- return cellContains(minLon, minLat, maxLon, maxLat) || cellWithin(minLon, minLat, maxLon, maxLat)
- || cellCrosses(minLon, minLat, maxLon, maxLat);
- }
-
- /**
- * The two-phase query approach. The parent
- * {@link org.apache.lucene.spatial.search.GeoPointTermsEnum#accept} method is called to match
- * encoded terms that fall within the bounding box of the polygon. Those documents that pass the initial
- * bounding box filter are then compared to the provided polygon using the
- * {@link org.apache.lucene.spatial.util.GeoRelationUtils#pointInPolygon} method.
- */
- @Override
- protected boolean postFilter(final double lon, final double lat) {
- return GeoRelationUtils.pointInPolygon(x, y, lat, lon);
- }
+ public Query rewrite(IndexReader reader) {
+ return new GeoPointInPolygonQueryImpl(field, termEncoding, this, this.minLon, this.minLat, this.maxLon, this.maxLat);
}
@Override
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/331c03dd/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQueryImpl.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQueryImpl.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQueryImpl.java
new file mode 100644
index 0000000..f0fe87b
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQueryImpl.java
@@ -0,0 +1,103 @@
+/*
+ * 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.search;
+
+import org.apache.lucene.search.MultiTermQuery;
+import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
+import org.apache.lucene.spatial.util.GeoRelationUtils;
+
+/** Package private implementation for the public facing GeoPointInPolygonQuery delegate class.
+ *
+ * @lucene.experimental
+ */
+final class GeoPointInPolygonQueryImpl extends GeoPointInBBoxQueryImpl {
+ private final GeoPointInPolygonQuery polygonQuery;
+
+ GeoPointInPolygonQueryImpl(final String field, final TermEncoding termEncoding, final GeoPointInPolygonQuery q,
+ final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ super(field, termEncoding, minLon, minLat, maxLon, maxLat);
+ polygonQuery = q;
+ }
+
+ @Override
+ public void setRewriteMethod(MultiTermQuery.RewriteMethod method) {
+ throw new UnsupportedOperationException("cannot change rewrite method");
+ }
+
+ @Override
+ protected CellComparator newCellComparator() {
+ return new GeoPolygonCellComparator(this);
+ }
+
+ /**
+ * Custom {@code org.apache.lucene.spatial.search.GeoPointMultiTermQuery.CellComparator} that computes morton hash
+ * ranges based on the defined edges of the provided polygon.
+ */
+ private final class GeoPolygonCellComparator extends CellComparator {
+ GeoPolygonCellComparator(GeoPointMultiTermQuery query) {
+ super(query);
+ }
+
+ @Override
+ protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ return GeoRelationUtils.rectCrossesPolyApprox(minLon, minLat, maxLon, maxLat, polygonQuery.x, polygonQuery.y,
+ polygonQuery.minLon, polygonQuery.minLat, polygonQuery.maxLon, polygonQuery.maxLat);
+ }
+
+ @Override
+ protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ return GeoRelationUtils.rectWithinPolyApprox(minLon, minLat, maxLon, maxLat, polygonQuery.x, polygonQuery.y,
+ polygonQuery.minLon, polygonQuery.minLat, polygonQuery.maxLon, polygonQuery.maxLat);
+ }
+
+ @Override
+ protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ return cellContains(minLon, minLat, maxLon, maxLat) || cellWithin(minLon, minLat, maxLon, maxLat)
+ || cellCrosses(minLon, minLat, maxLon, maxLat);
+ }
+
+ /**
+ * The two-phase query approach. The parent
+ * {@link org.apache.lucene.spatial.search.GeoPointTermsEnum#accept} method is called to match
+ * encoded terms that fall within the bounding box of the polygon. Those documents that pass the initial
+ * bounding box filter are then compared to the provided polygon using the
+ * {@link org.apache.lucene.spatial.util.GeoRelationUtils#pointInPolygon} method.
+ */
+ @Override
+ protected boolean postFilter(final double lon, final double lat) {
+ return GeoRelationUtils.pointInPolygon(polygonQuery.x, polygonQuery.y, lat, lon);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ GeoPointInPolygonQueryImpl that = (GeoPointInPolygonQueryImpl) o;
+
+ return !(polygonQuery != null ? !polygonQuery.equals(that.polygonQuery) : that.polygonQuery != null);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (polygonQuery != null ? polygonQuery.hashCode() : 0);
+ return result;
+ }
+}
[2/5] lucene-solr git commit: LUCENE-6930: Decouples GeoPointField
from NumericType by using a custom GeoPointTokenStream and TermEnum designed
for GeoPoint prefix terms
Posted by nk...@apache.org.
LUCENE-6930: Decouples GeoPointField from NumericType by using a custom GeoPointTokenStream and TermEnum designed for GeoPoint prefix terms
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/74a08c08
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/74a08c08
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/74a08c08
Branch: refs/heads/branch_5x
Commit: 74a08c08006941b74eda585b86b57fbe0ff341b2
Parents: aacda06
Author: nknize <nk...@apache.org>
Authored: Mon Feb 1 11:47:11 2016 -0600
Committer: nknize <nk...@apache.org>
Committed: Sat Feb 6 22:07:40 2016 -0600
----------------------------------------------------------------------
.../lucene/spatial/document/GeoPointField.java | 179 +++++++++++---
.../spatial/document/GeoPointTokenStream.java | 233 ++++++++++++++++++
.../spatial/search/GeoPointDistanceQuery.java | 24 +-
.../search/GeoPointDistanceQueryImpl.java | 64 ++---
.../search/GeoPointDistanceRangeQuery.java | 17 +-
.../spatial/search/GeoPointInBBoxQuery.java | 21 +-
.../spatial/search/GeoPointInBBoxQueryImpl.java | 63 +++--
.../spatial/search/GeoPointInPolygonQuery.java | 117 ++++-----
.../spatial/search/GeoPointMultiTermQuery.java | 166 +++++++++++++
.../search/GeoPointNumericTermsEnum.java | 161 +++++++++++++
.../spatial/search/GeoPointPrefixTermsEnum.java | 237 +++++++++++++++++++
.../spatial/search/GeoPointTermQuery.java | 114 ---------
.../GeoPointTermQueryConstantScoreWrapper.java | 11 +-
.../spatial/search/GeoPointTermsEnum.java | 199 ++++------------
.../lucene/spatial/util/GeoEncodingUtils.java | 157 ++++++++++++
.../lucene/spatial/util/GeoHashUtils.java | 8 +-
.../lucene/spatial/util/GeoRelationUtils.java | 16 +-
.../apache/lucene/spatial/util/GeoUtils.java | 87 +------
.../spatial/search/TestGeoPointQuery.java | 117 +++++----
.../spatial/util/BaseGeoPointTestCase.java | 4 +-
.../lucene/spatial/util/TestGeoUtils.java | 47 ++--
21 files changed, 1434 insertions(+), 608 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java
index ee94522..3cdd6ab 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java
@@ -20,7 +20,9 @@ import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.IndexOptions;
-import org.apache.lucene.spatial.util.GeoUtils;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
/**
* <p>
@@ -47,39 +49,83 @@ public final class GeoPointField extends Field {
public static final int PRECISION_STEP = 9;
/**
- * Type for an GeoPointField that is not stored:
+ * <b>Expert:</b> Optional flag to select term encoding for GeoPointField types
+ */
+ public enum TermEncoding {
+ /**
+ * encodes prefix terms only resulting in a small index and faster queries - use with
+ * {@link GeoPointTokenStream}
+ */
+ PREFIX,
+ /**
+ * @deprecated encodes prefix and full resolution terms - use with
+ * {@link org.apache.lucene.analysis.NumericTokenStream}
+ */
+ @Deprecated
+ NUMERIC
+ }
+
+ /**
+ * @deprecated Type for a GeoPointField that is not stored:
* normalization factors, frequencies, and positions are omitted.
*/
- public static final FieldType TYPE_NOT_STORED = new FieldType();
+ @Deprecated
+ public static final FieldType NUMERIC_TYPE_NOT_STORED = new FieldType();
static {
- TYPE_NOT_STORED.setTokenized(false);
- TYPE_NOT_STORED.setOmitNorms(true);
- TYPE_NOT_STORED.setIndexOptions(IndexOptions.DOCS);
- TYPE_NOT_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
- TYPE_NOT_STORED.setNumericType(FieldType.NumericType.LONG);
- TYPE_NOT_STORED.setNumericPrecisionStep(PRECISION_STEP);
- TYPE_NOT_STORED.freeze();
+ NUMERIC_TYPE_NOT_STORED.setTokenized(false);
+ NUMERIC_TYPE_NOT_STORED.setOmitNorms(true);
+ NUMERIC_TYPE_NOT_STORED.setIndexOptions(IndexOptions.DOCS);
+ NUMERIC_TYPE_NOT_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
+ NUMERIC_TYPE_NOT_STORED.setNumericType(FieldType.NumericType.LONG);
+ NUMERIC_TYPE_NOT_STORED.setNumericPrecisionStep(PRECISION_STEP);
+ NUMERIC_TYPE_NOT_STORED.freeze();
+ }
+
+ /**
+ * @deprecated Type for a stored GeoPointField:
+ * normalization factors, frequencies, and positions are omitted.
+ */
+ @Deprecated
+ public static final FieldType NUMERIC_TYPE_STORED = new FieldType();
+ static {
+ NUMERIC_TYPE_STORED.setTokenized(false);
+ NUMERIC_TYPE_STORED.setOmitNorms(true);
+ NUMERIC_TYPE_STORED.setIndexOptions(IndexOptions.DOCS);
+ NUMERIC_TYPE_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
+ NUMERIC_TYPE_STORED.setNumericType(FieldType.NumericType.LONG);
+ NUMERIC_TYPE_STORED.setNumericPrecisionStep(PRECISION_STEP);
+ NUMERIC_TYPE_STORED.setStored(true);
+ NUMERIC_TYPE_STORED.freeze();
+ }
+
+ /**
+ * Type for a GeoPointField that is not stored:
+ * normalization factors, frequencies, and positions are omitted.
+ */
+ public static final FieldType PREFIX_TYPE_NOT_STORED = new FieldType();
+ static {
+ PREFIX_TYPE_NOT_STORED.setTokenized(false);
+ PREFIX_TYPE_NOT_STORED.setOmitNorms(true);
+ PREFIX_TYPE_NOT_STORED.setIndexOptions(IndexOptions.DOCS);
+ PREFIX_TYPE_NOT_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
+ PREFIX_TYPE_NOT_STORED.freeze();
}
/**
* Type for a stored GeoPointField:
* normalization factors, frequencies, and positions are omitted.
*/
- public static final FieldType TYPE_STORED = new FieldType();
+ public static final FieldType PREFIX_TYPE_STORED = new FieldType();
static {
- TYPE_STORED.setTokenized(false);
- TYPE_STORED.setOmitNorms(true);
- TYPE_STORED.setIndexOptions(IndexOptions.DOCS);
- TYPE_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
- TYPE_STORED.setNumericType(FieldType.NumericType.LONG);
- TYPE_STORED.setNumericPrecisionStep(PRECISION_STEP);
- TYPE_STORED.setStored(true);
- TYPE_STORED.freeze();
+ PREFIX_TYPE_STORED.setTokenized(false);
+ PREFIX_TYPE_STORED.setOmitNorms(true);
+ PREFIX_TYPE_STORED.setIndexOptions(IndexOptions.DOCS);
+ PREFIX_TYPE_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
+ PREFIX_TYPE_STORED.setStored(true);
+ PREFIX_TYPE_STORED.freeze();
}
- /** Creates a stored or un-stored GeoPointField with the provided value
- * and default <code>precisionStep</code> set to 64 to avoid wasteful
- * indexing of lower precision terms.
+ /** Creates a stored or un-stored GeoPointField
* @param name field name
* @param lon longitude double value [-180.0 : 180.0]
* @param lat latitude double value [-90.0 : 90.0]
@@ -87,8 +133,20 @@ public final class GeoPointField extends Field {
* @throws IllegalArgumentException if the field name is null.
*/
public GeoPointField(String name, double lon, double lat, Store stored) {
- super(name, stored == Store.YES ? TYPE_STORED : TYPE_NOT_STORED);
- fieldsData = GeoUtils.mortonHash(lon, lat);
+ this(name, lon, lat, getFieldType(stored));
+ }
+
+ /** Creates a stored or un-stored GeoPointField using the specified {@link TermEncoding} method
+ * @param name field name
+ * @param lon longitude double value [-180.0 : 180.0]
+ * @param lat latitude double value [-90.0 : 90.0]
+ * @param termEncoding encoding type to use ({@link TermEncoding#NUMERIC} Terms, or {@link TermEncoding#PREFIX} only Terms)
+ * @param stored Store.YES if the content should also be stored
+ * @throws IllegalArgumentException if the field name is null.
+ */
+ @Deprecated
+ public GeoPointField(String name, double lon, double lat, TermEncoding termEncoding, Store stored) {
+ this(name, lon, lat, getFieldType(termEncoding, stored));
}
/** Expert: allows you to customize the {@link
@@ -103,23 +161,78 @@ public final class GeoPointField extends Field {
*/
public GeoPointField(String name, double lon, double lat, FieldType type) {
super(name, type);
- if (type.numericType() != FieldType.NumericType.LONG) {
- throw new IllegalArgumentException("type.numericType() must be LONG but got " + type.numericType());
+ // field must be indexed
+ // todo does it make sense here to provide the ability to store a GeoPointField but not index?
+ if (type.indexOptions() == IndexOptions.NONE && type.stored() == false) {
+ throw new IllegalArgumentException("type.indexOptions() is set to NONE but type.stored() is false");
+ } else if (type.indexOptions() == IndexOptions.DOCS) {
+ if (type.docValuesType() != DocValuesType.SORTED_NUMERIC) {
+ throw new IllegalArgumentException("type.docValuesType() must be SORTED_NUMERIC but got " + type.docValuesType());
+ }
+ if (type.numericType() != null) {
+ // make sure numericType is a LONG
+ if (type.numericType() != FieldType.NumericType.LONG) {
+ throw new IllegalArgumentException("type.numericType() must be LONG but got " + type.numericType());
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("type.indexOptions() must be one of NONE or DOCS but got " + type.indexOptions());
}
- if (type.docValuesType() != DocValuesType.SORTED_NUMERIC) {
- throw new IllegalArgumentException("type.docValuesType() must be SORTED_NUMERIC but got " + type.docValuesType());
+
+ // set field data
+ fieldsData = GeoEncodingUtils.mortonHash(lon, lat);
+ }
+
+ private static FieldType getFieldType(Store stored) {
+ return getFieldType(TermEncoding.PREFIX, stored);
+ }
+
+ /**
+ * @deprecated
+ * Static helper method for returning a valid FieldType based on termEncoding and stored options
+ */
+ @Deprecated
+ private static FieldType getFieldType(TermEncoding termEncoding, Store stored) {
+ if (stored == Store.YES) {
+ return termEncoding == TermEncoding.PREFIX ? PREFIX_TYPE_STORED : NUMERIC_TYPE_STORED;
+ } else if (stored == Store.NO) {
+ return termEncoding == TermEncoding.PREFIX ? PREFIX_TYPE_NOT_STORED : NUMERIC_TYPE_NOT_STORED;
+ } else {
+ throw new IllegalArgumentException("stored option must be NO or YES but got " + stored);
}
- fieldsData = GeoUtils.mortonHash(lon, lat);
+ }
+
+ @Override
+ public TokenStream tokenStream(Analyzer analyzer, TokenStream reuse) {
+ if (fieldType().indexOptions() == IndexOptions.NONE) {
+ // not indexed
+ return null;
+ }
+
+ // if numericType is set
+ if (type.numericType() != null) {
+ // return numeric encoding
+ return super.tokenStream(analyzer, reuse);
+ }
+
+ if (reuse instanceof GeoPointTokenStream == false) {
+ reuse = new GeoPointTokenStream();
+ }
+
+ final GeoPointTokenStream gpts = (GeoPointTokenStream)reuse;
+ gpts.setGeoCode(((Number) fieldsData).longValue());
+
+ return reuse;
}
/** access longitude value */
public double getLon() {
- return GeoUtils.mortonUnhashLon((long) fieldsData);
+ return GeoEncodingUtils.mortonUnhashLon((long) fieldsData);
}
/** access latitude value */
public double getLat() {
- return GeoUtils.mortonUnhashLat((long) fieldsData);
+ return GeoEncodingUtils.mortonUnhashLat((long) fieldsData);
}
@Override
@@ -128,9 +241,9 @@ public final class GeoPointField extends Field {
return null;
}
StringBuilder sb = new StringBuilder();
- sb.append(GeoUtils.mortonUnhashLon((long) fieldsData));
+ sb.append(GeoEncodingUtils.mortonUnhashLon((long) fieldsData));
sb.append(',');
- sb.append(GeoUtils.mortonUnhashLat((long) fieldsData));
+ sb.append(GeoEncodingUtils.mortonUnhashLat((long) fieldsData));
return sb.toString();
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
new file mode 100644
index 0000000..e22d446
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
@@ -0,0 +1,233 @@
+package org.apache.lucene.spatial.document;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Objects;
+
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
+import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
+import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute;
+import org.apache.lucene.util.Attribute;
+import org.apache.lucene.util.AttributeFactory;
+import org.apache.lucene.util.AttributeImpl;
+import org.apache.lucene.util.AttributeReflector;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
+
+import static org.apache.lucene.spatial.document.GeoPointField.PRECISION_STEP;
+
+/**
+ * <b>Expert:</b> This class provides a {@link TokenStream} used by {@link GeoPointField}
+ * for encoding {@link GeoPointField.TermEncoding#PREFIX} only GeoPointTerms.
+ *
+ * <p><i>NOTE: This is used as the default encoding unless
+ * {@code GeoPointField.setNumericType(FieldType.LegacyNumericType.LONG)} is set</i></p>
+ *
+ * This class is similar to {@link org.apache.lucene.analysis.LegacyNumericTokenStream} but encodes terms up to a
+ * a maximum of {@link #MAX_SHIFT} using a fixed precision step defined by
+ * {@link GeoPointField#PRECISION_STEP}. This yields a total of 4 terms per GeoPoint
+ * each consisting of 5 bytes (4 prefix bytes + 1 precision byte).
+ *
+ * <p>For best performance use the provided {@link GeoPointField#PREFIX_TYPE_NOT_STORED} or
+ * {@link GeoPointField#PREFIX_TYPE_STORED}</p>
+ *
+ * <p>If prefix terms are used then the default GeoPoint query constructors may be used, but if
+ * {@link org.apache.lucene.analysis.LegacyNumericTokenStream} is used, then be sure to pass
+ * {@link GeoPointField.TermEncoding#NUMERIC} to all GeoPointQuery constructors</p>
+ *
+ * Here's an example usage:
+ *
+ * <pre class="prettyprint">
+ * // using prefix terms
+ * GeoPointField geoPointField = new GeoPointField(fieldName1, lon, lat, GeoPointField.PREFIX_TYPE_NOT_STORED);
+ * document.add(geoPointField);
+ *
+ * // query by bounding box (default uses TermEncoding.PREFIX)
+ * Query q = new GeoPointInBBoxQuery(fieldName1, minLon, minLat, maxLon, maxLat);
+ *
+ * // using numeric terms
+ * geoPointField = new GeoPointField(fieldName2, lon, lat, GeoPointField.NUMERIC_TYPE_NOT_STORED);
+ * document.add(geoPointField);
+ *
+ * // query by distance (requires TermEncoding.NUMERIC)
+ * q = new GeoPointDistanceQuery(fieldName2, TermEncoding.NUMERIC, centerLon, centerLat, radiusMeters);
+ * </pre>
+ *
+ * @lucene.experimental
+ */
+final class GeoPointTokenStream extends TokenStream {
+ private static final int MAX_SHIFT = PRECISION_STEP * 4;
+
+ private final GeoPointTermAttribute geoPointTermAtt = addAttribute(GeoPointTermAttribute.class);
+ private final PositionIncrementAttribute posIncrAtt = addAttribute(PositionIncrementAttribute.class);
+
+ private boolean isInit = false;
+
+ /**
+ * Expert: Creates a token stream for geo point fields with the specified
+ * <code>precisionStep</code> using the given
+ * {@link org.apache.lucene.util.AttributeFactory}.
+ * The stream is not yet initialized,
+ * before using set a value using the various set<em>???</em>Value() methods.
+ */
+ public GeoPointTokenStream() {
+ super(new GeoPointAttributeFactory(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY));
+ assert PRECISION_STEP > 0;
+ }
+
+ public GeoPointTokenStream setGeoCode(final long geoCode) {
+ geoPointTermAtt.init(geoCode, MAX_SHIFT-PRECISION_STEP);
+ isInit = true;
+ return this;
+ }
+
+ @Override
+ public void reset() {
+ if (isInit == false) {
+ throw new IllegalStateException("call setGeoCode() before usage");
+ }
+ }
+
+ @Override
+ public boolean incrementToken() {
+ if (isInit == false) {
+ throw new IllegalStateException("call setGeoCode() before usage");
+ }
+
+ // this will only clear all other attributes in this TokenStream
+ clearAttributes();
+
+ final int shift = geoPointTermAtt.incShift();
+ posIncrAtt.setPositionIncrement((shift == MAX_SHIFT) ? 1 : 0);
+ return (shift < 63);
+ }
+
+ /**
+ * Tracks shift values during encoding
+ */
+ public interface GeoPointTermAttribute extends Attribute {
+ /** Returns current shift value, undefined before first token */
+ int getShift();
+
+ /** <em>Don't call this method!</em>
+ * @lucene.internal */
+ void init(long value, int shift);
+
+ /** <em>Don't call this method!</em>
+ * @lucene.internal */
+ int incShift();
+ }
+
+ // just a wrapper to prevent adding CTA
+ private static final class GeoPointAttributeFactory extends AttributeFactory {
+ private final AttributeFactory delegate;
+
+ GeoPointAttributeFactory(AttributeFactory delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) {
+ if (CharTermAttribute.class.isAssignableFrom(attClass)) {
+ throw new IllegalArgumentException("GeoPointTokenStream does not support CharTermAttribute.");
+ }
+ return delegate.createAttributeInstance(attClass);
+ }
+ }
+
+ public static final class GeoPointTermAttributeImpl extends AttributeImpl implements GeoPointTermAttribute,TermToBytesRefAttribute {
+ private long value = 0L;
+ private int shift = 0;
+ private BytesRefBuilder bytes = new BytesRefBuilder();
+
+ public GeoPointTermAttributeImpl() {
+ this.shift = MAX_SHIFT-PRECISION_STEP;
+ }
+
+ @Override
+ public BytesRef getBytesRef() {
+ GeoEncodingUtils.geoCodedToPrefixCoded(value, shift, bytes);
+ return bytes.get();
+ }
+
+ @Override
+ public void init(long value, int shift) {
+ this.value = value;
+ this.shift = shift;
+ }
+
+ @Override
+ public int getShift() { return shift; }
+
+ @Override
+ public int incShift() {
+ return (shift += PRECISION_STEP);
+ }
+
+ @Override
+ public void clear() {
+ // this attribute has no contents to clear!
+ // we keep it untouched as it's fully controlled by outer class.
+ }
+
+ @Override
+ public void reflectWith(AttributeReflector reflector) {
+ reflector.reflect(TermToBytesRefAttribute.class, "bytes", getBytesRef());
+ reflector.reflect(GeoPointTermAttribute.class, "shift", shift);
+ }
+
+ @Override
+ public void copyTo(AttributeImpl target) {
+ final GeoPointTermAttribute a = (GeoPointTermAttribute) target;
+ a.init(value, shift);
+ }
+
+ @Override
+ public GeoPointTermAttributeImpl clone() {
+ GeoPointTermAttributeImpl t = (GeoPointTermAttributeImpl)super.clone();
+ // Do a deep clone
+ t.bytes = new BytesRefBuilder();
+ t.bytes.copyBytes(getBytesRef());
+ return t;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(shift, value);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ GeoPointTermAttributeImpl other = (GeoPointTermAttributeImpl) obj;
+ if (shift != other.shift) return false;
+ if (value != other.value) return false;
+ return true;
+ }
+ }
+
+ /** override toString because it can throw cryptic "illegal shift value": */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "(precisionStep=" + PRECISION_STEP + " shift=" + geoPointTermAtt.getShift() + ")";
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQuery.java
index 26b0a24..60aadbc 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQuery.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQuery.java
@@ -22,6 +22,7 @@ import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
+import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
import org.apache.lucene.spatial.util.GeoDistanceUtils;
import org.apache.lucene.spatial.util.GeoRect;
import org.apache.lucene.spatial.util.GeoUtils;
@@ -34,7 +35,7 @@ import org.apache.lucene.spatial.util.GeoUtils;
* passing this initial filter are then passed to a secondary {@code postFilter} method that verifies whether the
* decoded lat/lon point fall within the specified query distance (see {@link org.apache.lucene.util.SloppyMath#haversin}.
* All morton value comparisons are subject to the same precision tolerance defined in
- * {@value org.apache.lucene.spatial.util.GeoUtils#TOLERANCE} and distance comparisons are subject to the accuracy of the
+ * {@value org.apache.lucene.spatial.util.GeoEncodingUtils#TOLERANCE} and distance comparisons are subject to the accuracy of the
* haversine formula (from R.W. Sinnott, "Virtues of the Haversine", Sky and Telescope, vol. 68, no. 2, 1984, p. 159)
*
* <p>Note: This query currently uses haversine which is a sloppy distance calculation (see above reference). For large
@@ -55,14 +56,20 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
* distance (in meters) from a given point
**/
public GeoPointDistanceQuery(final String field, final double centerLon, final double centerLat, final double radiusMeters) {
- this(field, GeoUtils.circleToBBox(centerLon, centerLat, radiusMeters), centerLon, centerLat, radiusMeters);
+ this(field, TermEncoding.PREFIX, centerLon, centerLat, radiusMeters);
}
- private GeoPointDistanceQuery(final String field, GeoRect bbox, final double centerLon,
+ public GeoPointDistanceQuery(final String field, final TermEncoding termEncoding, final double centerLon, final double centerLat, final double radiusMeters) {
+ this(field, termEncoding, GeoUtils.circleToBBox(centerLon, centerLat, radiusMeters), centerLon, centerLat, radiusMeters);
+ }
+
+ private GeoPointDistanceQuery(final String field, final TermEncoding termEncoding, final GeoRect bbox, final double centerLon,
final double centerLat, final double radiusMeters) {
- super(field, bbox.minLon, bbox.minLat, bbox.maxLon, bbox.maxLat);
+ super(field, termEncoding, bbox.minLon, bbox.minLat, bbox.maxLon, bbox.maxLat);
{
- // check longitudinal overlap (limits radius)
+ // check longitudinal overlap (restrict distance to maximum longitudinal radius)
+ // todo this restriction technically shouldn't be needed,
+ // its only purpose is to ensure the bounding box doesn't self overlap.
final double maxRadius = GeoDistanceUtils.maxRadialDistanceMeters(centerLon, centerLat);
if (radiusMeters > maxRadius) {
throw new IllegalArgumentException("radiusMeters " + radiusMeters + " exceeds maxRadius [" + maxRadius
@@ -102,7 +109,7 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
// unwrap left
unwrappedLon += -360.0D;
}
- GeoPointDistanceQueryImpl left = new GeoPointDistanceQueryImpl(field, this, unwrappedLon,
+ GeoPointDistanceQueryImpl left = new GeoPointDistanceQueryImpl(field, termEncoding, this, unwrappedLon,
new GeoRect(GeoUtils.MIN_LON_INCL, maxLon, minLat, maxLat));
bqb.add(new BooleanClause(left, BooleanClause.Occur.SHOULD));
@@ -110,13 +117,14 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
// unwrap right
unwrappedLon += 360.0D;
}
- GeoPointDistanceQueryImpl right = new GeoPointDistanceQueryImpl(field, this, unwrappedLon,
+ GeoPointDistanceQueryImpl right = new GeoPointDistanceQueryImpl(field, termEncoding, this, unwrappedLon,
new GeoRect(minLon, GeoUtils.MAX_LON_INCL, minLat, maxLat));
bqb.add(new BooleanClause(right, BooleanClause.Occur.SHOULD));
return bqb.build();
}
- return new GeoPointDistanceQueryImpl(field, this, centerLon, new GeoRect(this.minLon, this.maxLon, this.minLat, this.maxLat));
+ return new GeoPointDistanceQueryImpl(field, termEncoding, this, centerLon,
+ new GeoRect(this.minLon, this.maxLon, this.minLat, this.maxLat));
}
@Override
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQueryImpl.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQueryImpl.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQueryImpl.java
index 0777799..39fc696 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQueryImpl.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQueryImpl.java
@@ -16,13 +16,8 @@
*/
package org.apache.lucene.spatial.search;
-import java.io.IOException;
-
-import org.apache.lucene.index.Terms;
-import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.MultiTermQuery;
-import org.apache.lucene.util.AttributeSource;
-import org.apache.lucene.spatial.document.GeoPointField;
+import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
import org.apache.lucene.spatial.util.GeoRect;
import org.apache.lucene.spatial.util.GeoRelationUtils;
import org.apache.lucene.util.SloppyMath;
@@ -32,65 +27,46 @@ import org.apache.lucene.util.SloppyMath;
* @lucene.experimental
*/
final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
- private final GeoPointDistanceQuery query;
+ private final GeoPointDistanceQuery distanceQuery;
private final double centerLon;
- GeoPointDistanceQueryImpl(final String field, final GeoPointDistanceQuery q, final double centerLonUnwrapped,
- final GeoRect bbox) {
- super(field, bbox.minLon, bbox.minLat, bbox.maxLon, bbox.maxLat);
- query = q;
+ GeoPointDistanceQueryImpl(final String field, final TermEncoding termEncoding, final GeoPointDistanceQuery q,
+ final double centerLonUnwrapped, final GeoRect bbox) {
+ super(field, termEncoding, bbox.minLon, bbox.minLat, bbox.maxLon, bbox.maxLat);
+ distanceQuery = q;
centerLon = centerLonUnwrapped;
}
- @Override @SuppressWarnings("unchecked")
- protected TermsEnum getTermsEnum(final Terms terms, AttributeSource atts) throws IOException {
- return new GeoPointRadiusTermsEnum(terms.iterator(), this.minLon, this.minLat, this.maxLon, this.maxLat);
- }
-
@Override
public void setRewriteMethod(MultiTermQuery.RewriteMethod method) {
throw new UnsupportedOperationException("cannot change rewrite method");
}
- private final class GeoPointRadiusTermsEnum extends GeoPointTermsEnum {
- GeoPointRadiusTermsEnum(final TermsEnum tenum, final double minLon, final double minLat,
- final double maxLon, final double maxLat) {
- super(tenum, minLon, minLat, maxLon, maxLat);
- }
-
- /**
- * Computes the maximum shift for the given pointDistanceQuery. This prevents unnecessary depth traversal
- * given the size of the distance query.
- */
- @Override
- protected short computeMaxShift() {
- final short shiftFactor;
-
- if (query.radiusMeters > 1000000) {
- shiftFactor = 5;
- } else {
- shiftFactor = 4;
- }
+ @Override
+ protected CellComparator newCellComparator() {
+ return new GeoPointRadiusCellComparator(this);
+ }
- return (short)(GeoPointField.PRECISION_STEP * shiftFactor);
+ private final class GeoPointRadiusCellComparator extends CellComparator {
+ GeoPointRadiusCellComparator(GeoPointDistanceQueryImpl query) {
+ super(query);
}
@Override
protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat) {
return GeoRelationUtils.rectCrossesCircle(minLon, minLat, maxLon, maxLat,
- centerLon, query.centerLat, query.radiusMeters, true);
+ centerLon, distanceQuery.centerLat, distanceQuery.radiusMeters, true);
}
@Override
protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat) {
return GeoRelationUtils.rectWithinCircle(minLon, minLat, maxLon, maxLat,
- centerLon, query.centerLat, query.radiusMeters, true);
+ centerLon, distanceQuery.centerLat, distanceQuery.radiusMeters, true);
}
@Override
protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat) {
- return (cellContains(minLon, minLat, maxLon, maxLat)
- || cellWithin(minLon, minLat, maxLon, maxLat) || cellCrosses(minLon, minLat, maxLon, maxLat));
+ return cellCrosses(minLon, minLat, maxLon, maxLat);
}
/**
@@ -101,7 +77,7 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
*/
@Override
protected boolean postFilter(final double lon, final double lat) {
- return (SloppyMath.haversin(query.centerLat, centerLon, lat, lon) * 1000.0 <= query.radiusMeters);
+ return (SloppyMath.haversin(distanceQuery.centerLat, centerLon, lat, lon) * 1000.0 <= distanceQuery.radiusMeters);
}
}
@@ -113,7 +89,7 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
GeoPointDistanceQueryImpl that = (GeoPointDistanceQueryImpl) o;
- if (!query.equals(that.query)) return false;
+ if (!distanceQuery.equals(that.distanceQuery)) return false;
return true;
}
@@ -121,11 +97,11 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
@Override
public int hashCode() {
int result = super.hashCode();
- result = 31 * result + query.hashCode();
+ result = 31 * result + distanceQuery.hashCode();
return result;
}
public double getRadiusMeters() {
- return query.getRadiusMeters();
+ return distanceQuery.getRadiusMeters();
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceRangeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceRangeQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceRangeQuery.java
index 2173054..59a2da7 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceRangeQuery.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceRangeQuery.java
@@ -22,6 +22,7 @@ import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
+import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
/** Implements a point distance range query on a GeoPoint field. This is based on
* {@code org.apache.lucene.spatial.search.GeoPointDistanceQuery} and is implemented using a
@@ -38,8 +39,13 @@ public final class GeoPointDistanceRangeQuery extends GeoPointDistanceQuery {
* distance (in meters) range from a given point
*/
public GeoPointDistanceRangeQuery(final String field, final double centerLon, final double centerLat,
+ final double minRadiusMeters, final double maxRadiusMeters) {
+ this(field, TermEncoding.PREFIX, centerLon, centerLat, minRadiusMeters, maxRadiusMeters);
+ }
+
+ public GeoPointDistanceRangeQuery(final String field, final TermEncoding termEncoding, final double centerLon, final double centerLat,
final double minRadiusMeters, final double maxRadius) {
- super(field, centerLon, centerLat, maxRadius);
+ super(field, termEncoding, centerLon, centerLat, maxRadius);
this.minRadiusMeters = minRadiusMeters;
}
@@ -57,8 +63,13 @@ public final class GeoPointDistanceRangeQuery extends GeoPointDistanceQuery {
BooleanQuery.Builder bqb = new BooleanQuery.Builder();
// create a new exclusion query
- GeoPointDistanceQuery exclude = new GeoPointDistanceQuery(field, centerLon, centerLat, minRadiusMeters);
- bqb.add(new BooleanClause(q, BooleanClause.Occur.MUST));
+ GeoPointDistanceQuery exclude = new GeoPointDistanceQuery(field, termEncoding, centerLon, centerLat, minRadiusMeters);
+ // full map search
+// if (radiusMeters >= GeoProjectionUtils.SEMIMINOR_AXIS) {
+// bqb.add(new BooleanClause(new GeoPointInBBoxQuery(this.field, -180.0, -90.0, 180.0, 90.0), BooleanClause.Occur.MUST));
+// } else {
+ bqb.add(new BooleanClause(q, BooleanClause.Occur.MUST));
+// }
bqb.add(new BooleanClause(exclude, BooleanClause.Occur.MUST_NOT));
return bqb.build();
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java
index a216aa0..b7fcf70 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java
@@ -24,6 +24,7 @@ import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.FieldValueQuery;
import org.apache.lucene.search.Query;
+import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
import org.apache.lucene.spatial.util.GeoUtils;
/** Implements a simple bounding box query on a GeoPoint field. This is inspired by
@@ -51,17 +52,23 @@ public class GeoPointInBBoxQuery extends Query {
protected final double minLat;
protected final double maxLon;
protected final double maxLat;
+ protected final TermEncoding termEncoding;
/**
* Constructs a query for all {@link org.apache.lucene.spatial.document.GeoPointField} types that fall within a
* defined bounding box
*/
public GeoPointInBBoxQuery(final String field, final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ this(field, TermEncoding.PREFIX, minLon, minLat, maxLon, maxLat);
+ }
+
+ public GeoPointInBBoxQuery(final String field, final TermEncoding termEncoding, final double minLon, final double minLat, final double maxLon, final double maxLat) {
this.field = field;
this.minLon = minLon;
this.minLat = minLat;
this.maxLon = maxLon;
this.maxLat = maxLat;
+ this.termEncoding = termEncoding;
}
@Override
@@ -78,15 +85,15 @@ public class GeoPointInBBoxQuery extends Query {
}
if (maxLon < minLon) {
- BooleanQuery.Builder bq = new BooleanQuery.Builder();
+ BooleanQuery.Builder bqb = new BooleanQuery.Builder();
- GeoPointInBBoxQueryImpl left = new GeoPointInBBoxQueryImpl(field, -180.0D, minLat, maxLon, maxLat);
- bq.add(new BooleanClause(left, BooleanClause.Occur.SHOULD));
- GeoPointInBBoxQueryImpl right = new GeoPointInBBoxQueryImpl(field, minLon, minLat, 180.0D, maxLat);
- bq.add(new BooleanClause(right, BooleanClause.Occur.SHOULD));
- return bq.build();
+ GeoPointInBBoxQueryImpl left = new GeoPointInBBoxQueryImpl(field, termEncoding, -180.0D, minLat, maxLon, maxLat);
+ bqb.add(new BooleanClause(left, BooleanClause.Occur.SHOULD));
+ GeoPointInBBoxQueryImpl right = new GeoPointInBBoxQueryImpl(field, termEncoding, minLon, minLat, 180.0D, maxLat);
+ bqb.add(new BooleanClause(right, BooleanClause.Occur.SHOULD));
+ return bqb.build();
}
- return new GeoPointInBBoxQueryImpl(field, minLon, minLat, maxLon, maxLat);
+ return new GeoPointInBBoxQueryImpl(field, termEncoding, minLon, minLat, maxLon, maxLat);
}
@Override
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQueryImpl.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQueryImpl.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQueryImpl.java
index bd44b1e..8bce0f0 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQueryImpl.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQueryImpl.java
@@ -16,21 +16,17 @@
*/
package org.apache.lucene.spatial.search;
-import java.io.IOException;
-
-import org.apache.lucene.index.Terms;
-import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.MultiTermQuery;
-import org.apache.lucene.util.AttributeSource;
import org.apache.lucene.util.SloppyMath;
import org.apache.lucene.spatial.document.GeoPointField;
+import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
import org.apache.lucene.spatial.util.GeoRelationUtils;
/** Package private implementation for the public facing GeoPointInBBoxQuery delegate class.
*
* @lucene.experimental
*/
-class GeoPointInBBoxQueryImpl extends GeoPointTermQuery {
+class GeoPointInBBoxQueryImpl extends GeoPointMultiTermQuery {
/**
* Constructs a new GeoBBoxQuery that will match encoded GeoPoint terms that fall within or on the boundary
* of the bounding box defined by the input parameters
@@ -40,13 +36,8 @@ class GeoPointInBBoxQueryImpl extends GeoPointTermQuery {
* @param maxLon upper longitude (x) value of the bounding box
* @param maxLat upper latitude (y) value of the bounding box
*/
- GeoPointInBBoxQueryImpl(final String field, final double minLon, final double minLat, final double maxLon, final double maxLat) {
- super(field, minLon, minLat, maxLon, maxLat);
- }
-
- @Override @SuppressWarnings("unchecked")
- protected TermsEnum getTermsEnum(final Terms terms, AttributeSource atts) throws IOException {
- return new GeoPointInBBoxTermsEnum(terms.iterator(), minLon, minLat, maxLon, maxLat);
+ GeoPointInBBoxQueryImpl(final String field, final TermEncoding termEncoding, final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ super(field, termEncoding, minLon, minLat, maxLon, maxLat);
}
@Override
@@ -54,27 +45,31 @@ class GeoPointInBBoxQueryImpl extends GeoPointTermQuery {
throw new UnsupportedOperationException("cannot change rewrite method");
}
- protected class GeoPointInBBoxTermsEnum extends GeoPointTermsEnum {
- protected GeoPointInBBoxTermsEnum(final TermsEnum tenum, final double minLon, final double minLat,
- final double maxLon, final double maxLat) {
- super(tenum, minLon, minLat, maxLon, maxLat);
- }
+ @Override
+ protected short computeMaxShift() {
+ final short shiftFactor;
- @Override
- protected short computeMaxShift() {
- final short shiftFactor;
+ // compute diagonal radius
+ double midLon = (minLon + maxLon) * 0.5;
+ double midLat = (minLat + maxLat) * 0.5;
- // compute diagonal radius
- double midLon = (minLon + maxLon) * 0.5;
- double midLat = (minLat + maxLat) * 0.5;
+ if (SloppyMath.haversin(minLat, minLon, midLat, midLon)*1000 > 1000000) {
+ shiftFactor = 5;
+ } else {
+ shiftFactor = 4;
+ }
- if (SloppyMath.haversin(minLat, minLon, midLat, midLon)*1000 > 1000000) {
- shiftFactor = 5;
- } else {
- shiftFactor = 4;
- }
+ return (short)(GeoPointField.PRECISION_STEP * shiftFactor);
+ }
+
+ @Override
+ protected CellComparator newCellComparator() {
+ return new GeoPointInBBoxCellComparator(this);
+ }
- return (short)(GeoPointField.PRECISION_STEP * shiftFactor);
+ private final class GeoPointInBBoxCellComparator extends CellComparator {
+ GeoPointInBBoxCellComparator(GeoPointMultiTermQuery query) {
+ super(query);
}
/**
@@ -82,16 +77,16 @@ class GeoPointInBBoxQueryImpl extends GeoPointTermQuery {
*/
@Override
protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat) {
- return GeoRelationUtils.rectCrosses(minLon, minLat, maxLon, maxLat, this.minLon, this.minLat, this.maxLon, this.maxLat);
- }
+ return GeoRelationUtils.rectCrosses(minLon, minLat, maxLon, maxLat, GeoPointInBBoxQueryImpl.this.minLon,
+ GeoPointInBBoxQueryImpl.this.minLat, GeoPointInBBoxQueryImpl.this.maxLon, GeoPointInBBoxQueryImpl.this.maxLat); }
/**
* Determine whether quad-cell is within the shape
*/
@Override
protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat) {
- return GeoRelationUtils.rectWithin(minLon, minLat, maxLon, maxLat, this.minLon, this.minLat, this.maxLon, this.maxLat);
- }
+ return GeoRelationUtils.rectWithin(minLon, minLat, maxLon, maxLat, GeoPointInBBoxQueryImpl.this.minLon,
+ GeoPointInBBoxQueryImpl.this.minLat, GeoPointInBBoxQueryImpl.this.maxLon, GeoPointInBBoxQueryImpl.this.maxLat); }
@Override
protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat) {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
index b1e864b..a9be21e 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
@@ -22,6 +22,8 @@ import java.util.Arrays;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.util.AttributeSource;
+import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
import org.apache.lucene.spatial.util.GeoRect;
import org.apache.lucene.spatial.util.GeoRelationUtils;
import org.apache.lucene.spatial.util.GeoUtils;
@@ -34,7 +36,11 @@ import org.apache.lucene.spatial.util.GeoUtils;
* to a secondary filter that verifies whether the decoded lat/lon point falls within
* (or on the boundary) of the bounding box query. Finally, the remaining candidate
* term is passed to the final point in polygon check. All value comparisons are subject
+<<<<<<< HEAD:lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
* to the same precision tolerance defined in {@value org.apache.lucene.spatial.util.GeoUtils#TOLERANCE}
+=======
+ * to the same precision tolerance defined in {@value GeoEncodingUtils#TOLERANCE}
+>>>>>>> LUCENE-6930: Decouples GeoPointField from NumericType by using a custom GeoPointTokenStream and TermEnum designed for GeoPoint prefix terms:lucene/sandbox/src/java/org/apache/lucene/search/GeoPointInPolygonQuery.java
*
* <p>NOTES:
* 1. The polygon coordinates need to be in either clockwise or counter-clockwise order.
@@ -51,17 +57,21 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQueryImpl {
private final double[] x;
private final double[] y;
+ public GeoPointInPolygonQuery(final String field, final double[] polyLons, final double[] polyLats) {
+ this(field, TermEncoding.PREFIX, GeoUtils.polyToBBox(polyLons, polyLats), polyLons, polyLats);
+ }
+
/**
* Constructs a new GeoPolygonQuery that will match encoded {@link org.apache.lucene.spatial.document.GeoPointField} terms
* that fall within or on the boundary of the polygon defined by the input parameters.
*/
- public GeoPointInPolygonQuery(final String field, final double[] polyLons, final double[] polyLats) {
- this(field, GeoUtils.polyToBBox(polyLons, polyLats), polyLons, polyLats);
+ public GeoPointInPolygonQuery(final String field, final TermEncoding termEncoding, final double[] polyLons, final double[] polyLats) {
+ this(field, termEncoding, GeoUtils.polyToBBox(polyLons, polyLats), polyLons, polyLats);
}
/** Common constructor, used only internally. */
- private GeoPointInPolygonQuery(final String field, GeoRect bbox, final double[] polyLons, final double[] polyLats) {
- super(field, bbox.minLon, bbox.minLat, bbox.maxLon, bbox.maxLat);
+ private GeoPointInPolygonQuery(final String field, TermEncoding termEncoding, GeoRect bbox, final double[] polyLons, final double[] polyLats) {
+ super(field, termEncoding, bbox.minLon, bbox.minLat, bbox.maxLon, bbox.maxLat);
if (polyLats.length != polyLons.length) {
throw new IllegalArgumentException("polyLats and polyLons must be equal length");
}
@@ -79,11 +89,6 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQueryImpl {
this.y = polyLats;
}
- @Override @SuppressWarnings("unchecked")
- protected TermsEnum getTermsEnum(final Terms terms, AttributeSource atts) throws IOException {
- return new GeoPolygonTermsEnum(terms.iterator(), this.minLon, this.minLat, this.maxLon, this.maxLat);
- }
-
/** throw exception if trying to change rewrite method */
@Override
public void setRewriteMethod(RewriteMethod method) {
@@ -91,6 +96,51 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQueryImpl {
}
@Override
+ protected CellComparator newCellComparator() {
+ return new GeoPolygonCellComparator(this);
+ }
+
+ /**
+ * Custom {@link org.apache.lucene.index.TermsEnum} that computes morton hash ranges based on the defined edges of
+ * the provided polygon.
+ */
+ private final class GeoPolygonCellComparator extends CellComparator {
+ GeoPolygonCellComparator(GeoPointMultiTermQuery query) {
+ super(query);
+ }
+
+ @Override
+ protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ return GeoRelationUtils.rectCrossesPolyApprox(minLon, minLat, maxLon, maxLat, x, y, GeoPointInPolygonQuery.this.minLon,
+ GeoPointInPolygonQuery.this.minLat, GeoPointInPolygonQuery.this.maxLon, GeoPointInPolygonQuery.this.maxLat);
+ }
+
+ @Override
+ protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ return GeoRelationUtils.rectWithinPolyApprox(minLon, minLat, maxLon, maxLat, x, y, GeoPointInPolygonQuery.this.minLon,
+ GeoPointInPolygonQuery.this.minLat, GeoPointInPolygonQuery.this.maxLon, GeoPointInPolygonQuery.this.maxLat);
+ }
+
+ @Override
+ protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ return cellContains(minLon, minLat, maxLon, maxLat) || cellWithin(minLon, minLat, maxLon, maxLat)
+ || cellCrosses(minLon, minLat, maxLon, maxLat);
+ }
+
+ /**
+ * The two-phase query approach. The parent
+ * {@link org.apache.lucene.spatial.search.GeoPointTermsEnum#accept} method is called to match
+ * encoded terms that fall within the bounding box of the polygon. Those documents that pass the initial
+ * bounding box filter are then compared to the provided polygon using the
+ * {@link org.apache.lucene.spatial.util.GeoRelationUtils#pointInPolygon} method.
+ */
+ @Override
+ protected boolean postFilter(final double lon, final double lat) {
+ return GeoRelationUtils.pointInPolygon(x, y, lat, lon);
+ }
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@@ -128,57 +178,16 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQueryImpl {
sb.append(" Points: ");
for (int i=0; i<x.length; ++i) {
sb.append("[")
- .append(x[i])
- .append(", ")
- .append(y[i])
- .append("] ");
+ .append(x[i])
+ .append(", ")
+ .append(y[i])
+ .append("] ");
}
return sb.toString();
}
/**
- * Custom {@link org.apache.lucene.index.TermsEnum} that computes morton hash ranges based on the defined edges of
- * the provided polygon.
- */
- private final class GeoPolygonTermsEnum extends GeoPointTermsEnum {
- GeoPolygonTermsEnum(final TermsEnum tenum, final double minLon, final double minLat,
- final double maxLon, final double maxLat) {
- super(tenum, minLon, minLat, maxLon, maxLat);
- }
-
- @Override
- protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat) {
- return GeoRelationUtils.rectCrossesPolyApprox(minLon, minLat, maxLon, maxLat, x, y, GeoPointInPolygonQuery.this.minLon,
- GeoPointInPolygonQuery.this.minLat, GeoPointInPolygonQuery.this.maxLon, GeoPointInPolygonQuery.this.maxLat);
- }
-
- @Override
- protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat) {
- return GeoRelationUtils.rectWithinPolyApprox(minLon, minLat, maxLon, maxLat, x, y, GeoPointInPolygonQuery.this.minLon,
- GeoPointInPolygonQuery.this.minLat, GeoPointInPolygonQuery.this.maxLon, GeoPointInPolygonQuery.this.maxLat);
- }
-
- @Override
- protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat) {
- return cellContains(minLon, minLat, maxLon, maxLat) || cellWithin(minLon, minLat, maxLon, maxLat)
- || cellCrosses(minLon, minLat, maxLon, maxLat);
- }
-
- /**
- * The two-phase query approach. The parent
- * {@link GeoPointTermsEnum#accept} method is called to match
- * encoded terms that fall within the bounding box of the polygon. Those documents that pass the initial
- * bounding box filter are then compared to the provided polygon using the
- * {@link org.apache.lucene.spatial.util.GeoRelationUtils#pointInPolygon} method.
- */
- @Override
- protected boolean postFilter(final double lon, final double lat) {
- return GeoRelationUtils.pointInPolygon(x, y, lat, lon);
- }
- }
-
- /**
* API utility method for returning the array of longitudinal values for this GeoPolygon
* The returned array is not a copy so do not change it!
*/
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointMultiTermQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointMultiTermQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointMultiTermQuery.java
new file mode 100644
index 0000000..f11c4a6
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointMultiTermQuery.java
@@ -0,0 +1,166 @@
+package org.apache.lucene.spatial.search;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Terms;
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.search.MultiTermQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.AttributeSource;
+import org.apache.lucene.spatial.document.GeoPointField;
+import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
+import org.apache.lucene.spatial.util.GeoRelationUtils;
+import org.apache.lucene.spatial.util.GeoUtils;
+import org.apache.lucene.util.SloppyMath;
+
+/**
+ * TermQuery for GeoPointField for overriding {@link org.apache.lucene.search.MultiTermQuery} methods specific to
+ * Geospatial operations
+ *
+ * @lucene.experimental
+ */
+abstract class GeoPointMultiTermQuery extends MultiTermQuery {
+ // simple bounding box optimization - no objects used to avoid dependencies
+ protected final double minLon;
+ protected final double minLat;
+ protected final double maxLon;
+ protected final double maxLat;
+ protected final short maxShift;
+ protected final TermEncoding termEncoding;
+ protected final CellComparator cellComparator;
+
+ /**
+ * Constructs a query matching terms that cannot be represented with a single
+ * Term.
+ */
+ public GeoPointMultiTermQuery(String field, final TermEncoding termEncoding, final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ super(field);
+
+ if (GeoUtils.isValidLon(minLon) == false) {
+ throw new IllegalArgumentException("invalid minLon " + minLon);
+ }
+ if (GeoUtils.isValidLon(maxLon) == false) {
+ throw new IllegalArgumentException("invalid maxLon " + maxLon);
+ }
+ if (GeoUtils.isValidLat(minLat) == false) {
+ throw new IllegalArgumentException("invalid minLat " + minLat);
+ }
+ if (GeoUtils.isValidLat(maxLat) == false) {
+ throw new IllegalArgumentException("invalid maxLat " + maxLat);
+ }
+
+ final long minHash = GeoEncodingUtils.mortonHash(minLon, minLat);
+ final long maxHash = GeoEncodingUtils.mortonHash(maxLon, maxLat);
+ this.minLon = GeoEncodingUtils.mortonUnhashLon(minHash);
+ this.minLat = GeoEncodingUtils.mortonUnhashLat(minHash);
+ this.maxLon = GeoEncodingUtils.mortonUnhashLon(maxHash);
+ this.maxLat = GeoEncodingUtils.mortonUnhashLat(maxHash);
+
+ this.maxShift = computeMaxShift();
+ this.termEncoding = termEncoding;
+ this.cellComparator = newCellComparator();
+
+ this.rewriteMethod = GEO_CONSTANT_SCORE_REWRITE;
+ }
+
+ public static final RewriteMethod GEO_CONSTANT_SCORE_REWRITE = new RewriteMethod() {
+ @Override
+ public Query rewrite(IndexReader reader, MultiTermQuery query) {
+ return new GeoPointTermQueryConstantScoreWrapper<>((GeoPointMultiTermQuery)query);
+ }
+ };
+
+ @Override @SuppressWarnings("unchecked")
+ protected TermsEnum getTermsEnum(final Terms terms, AttributeSource atts) throws IOException {
+ return GeoPointTermsEnum.newInstance(terms.iterator(), this);
+ }
+
+ /**
+ * Computes the maximum shift based on the diagonal distance of the bounding box
+ */
+ protected short computeMaxShift() {
+ // in this case a factor of 4 brings the detail level to ~0.002/0.001 degrees lon/lat respectively (or ~222m/111m)
+ final short shiftFactor;
+
+ // compute diagonal distance
+ double midLon = (minLon + maxLon) * 0.5;
+ double midLat = (minLat + maxLat) * 0.5;
+
+ if (SloppyMath.haversin(minLat, minLon, midLat, midLon)*1000 > 1000000) {
+ shiftFactor = 5;
+ } else {
+ shiftFactor = 4;
+ }
+
+ return (short)(GeoPointField.PRECISION_STEP * shiftFactor);
+ }
+
+ /**
+ * Abstract method to construct the class that handles all geo point relations
+ * (e.g., GeoPointInPolygon)
+ */
+ abstract protected CellComparator newCellComparator();
+
+ /**
+ * Base class for all geo point relation comparators
+ */
+ static abstract class CellComparator {
+ protected final GeoPointMultiTermQuery geoPointQuery;
+
+ CellComparator(GeoPointMultiTermQuery query) {
+ this.geoPointQuery = query;
+ }
+
+ /**
+ * Primary driver for cells intersecting shape boundaries
+ */
+ protected boolean cellIntersectsMBR(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ return GeoRelationUtils.rectIntersects(minLon, minLat, maxLon, maxLat, geoPointQuery.minLon, geoPointQuery.minLat,
+ geoPointQuery.maxLon, geoPointQuery.maxLat);
+ }
+
+ /**
+ * Return whether quad-cell contains the bounding box of this shape
+ */
+ protected boolean cellContains(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ return GeoRelationUtils.rectWithin(geoPointQuery.minLon, geoPointQuery.minLat, geoPointQuery.maxLon,
+ geoPointQuery.maxLat, minLon, minLat, maxLon, maxLat);
+ }
+
+ /**
+ * Determine whether the quad-cell crosses the shape
+ */
+ abstract protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat);
+
+ /**
+ * Determine whether quad-cell is within the shape
+ */
+ abstract protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat);
+
+ /**
+ * Default shape is a rectangle, so this returns the same as {@code cellIntersectsMBR}
+ */
+ abstract protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat);
+
+ abstract protected boolean postFilter(final double lon, final double lat);
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointNumericTermsEnum.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointNumericTermsEnum.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointNumericTermsEnum.java
new file mode 100644
index 0000000..566d917
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointNumericTermsEnum.java
@@ -0,0 +1,161 @@
+package org.apache.lucene.spatial.search;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.spatial.document.GeoPointField;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.util.NumericUtils;
+
+/**
+ * Decomposes a given {@link GeoPointMultiTermQuery} into a set of terms that represent the query criteria using
+ * {@link import org.apache.lucene.document.GeoPointTokenStream.TermEncoding#NUMERIC} method defined by
+ * {@link org.apache.lucene.analysis.NumericTokenStream}. The terms are then enumerated by the
+ * {@link GeoPointTermQueryConstantScoreWrapper} and all docs whose GeoPoint fields match the prefix terms or
+ * pass the {@link GeoPointMultiTermQuery.CellComparator#postFilter} criteria are returned in the resulting DocIdSet.
+ *
+ * @lucene.experimental
+ */
+@Deprecated
+final class GeoPointNumericTermsEnum extends GeoPointTermsEnum {
+ private final List<Range> rangeBounds = new LinkedList<>();
+
+ // detail level should be a factor of PRECISION_STEP limiting the depth of recursion (and number of ranges)
+ private final short DETAIL_LEVEL;
+
+ GeoPointNumericTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
+ super(tenum, query);
+ DETAIL_LEVEL = (short)(((GeoEncodingUtils.BITS<<1)-this.maxShift)/2);
+ computeRange(0L, (short) (((GeoEncodingUtils.BITS) << 1) - 1));
+ assert rangeBounds.isEmpty() == false;
+ Collections.sort(rangeBounds);
+ }
+
+ /**
+ * entry point for recursively computing ranges
+ */
+ private final void computeRange(long term, final short shift) {
+ final long split = term | (0x1L<<shift);
+ assert shift < 64;
+ final long upperMax;
+ if (shift < 63) {
+ upperMax = term | ((1L << (shift+1))-1);
+ } else {
+ upperMax = 0xffffffffffffffffL;
+ }
+ final long lowerMax = split-1;
+
+ relateAndRecurse(term, lowerMax, shift);
+ relateAndRecurse(split, upperMax, shift);
+ }
+
+ /**
+ * recurse to higher level precision cells to find ranges along the space-filling curve that fall within the
+ * query box
+ *
+ * @param start starting value on the space-filling curve for a cell at a given res
+ * @param end ending value on the space-filling curve for a cell at a given res
+ * @param res spatial res represented as a bit shift (MSB is lower res)
+ */
+ private void relateAndRecurse(final long start, final long end, final short res) {
+ final double minLon = GeoEncodingUtils.mortonUnhashLon(start);
+ final double minLat = GeoEncodingUtils.mortonUnhashLat(start);
+ final double maxLon = GeoEncodingUtils.mortonUnhashLon(end);
+ final double maxLat = GeoEncodingUtils.mortonUnhashLat(end);
+
+ final short level = (short)((GeoEncodingUtils.BITS<<1)-res>>>1);
+
+ // if cell is within and a factor of the precision step, or it crosses the edge of the shape add the range
+ final boolean within = res % GeoPointField.PRECISION_STEP == 0 && relationImpl.cellWithin(minLon, minLat, maxLon, maxLat);
+ if (within || (level == DETAIL_LEVEL && relationImpl.cellIntersectsShape(minLon, minLat, maxLon, maxLat))) {
+ final short nextRes = (short)(res-1);
+ if (nextRes % GeoPointField.PRECISION_STEP == 0) {
+ rangeBounds.add(new Range(start, nextRes, !within));
+ rangeBounds.add(new Range(start|(1L<<nextRes), nextRes, !within));
+ } else {
+ rangeBounds.add(new Range(start, res, !within));
+ }
+ } else if (level < DETAIL_LEVEL && relationImpl.cellIntersectsMBR(minLon, minLat, maxLon, maxLat)) {
+ computeRange(start, (short) (res - 1));
+ }
+ }
+
+ @Override
+ protected final BytesRef peek() {
+ rangeBounds.get(0).fillBytesRef(this.nextSubRangeBRB);
+ return nextSubRangeBRB.get();
+ }
+
+ @Override
+ protected void nextRange() {
+ currentRange = rangeBounds.remove(0);
+ super.nextRange();
+ }
+
+ @Override
+ protected final BytesRef nextSeekTerm(BytesRef term) {
+ while (hasNext()) {
+ if (currentRange == null) {
+ nextRange();
+ }
+ // if the new upper bound is before the term parameter, the sub-range is never a hit
+ if (term != null && term.compareTo(currentCell) > 0) {
+ nextRange();
+ if (!rangeBounds.isEmpty()) {
+ continue;
+ }
+ }
+ // never seek backwards, so use current term if lower bound is smaller
+ return (term != null && term.compareTo(currentCell) > 0) ? term : currentCell;
+ }
+
+ // no more sub-range enums available
+ assert rangeBounds.isEmpty();
+ return null;
+ }
+
+ @Override
+ protected final boolean hasNext() {
+ return rangeBounds.isEmpty() == false;
+ }
+
+ /**
+ * Internal class to represent a range along the space filling curve
+ */
+ protected final class Range extends BaseRange {
+ Range(final long lower, final short shift, boolean boundary) {
+ super(lower, shift, boundary);
+ }
+
+ /**
+ * Encode as a BytesRef using a reusable object. This allows us to lazily create the BytesRef (which is
+ * quite expensive), only when we need it.
+ */
+ @Override
+ protected void fillBytesRef(BytesRefBuilder result) {
+ assert result != null;
+ NumericUtils.longToPrefixCoded(start, shift, result);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointPrefixTermsEnum.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointPrefixTermsEnum.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointPrefixTermsEnum.java
new file mode 100644
index 0000000..239e959
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointPrefixTermsEnum.java
@@ -0,0 +1,237 @@
+package org.apache.lucene.spatial.search;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.spatial.document.GeoPointField;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
+
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.mortonHash;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.mortonUnhashLat;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.mortonUnhashLon;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.geoCodedToPrefixCoded;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.prefixCodedToGeoCoded;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.getPrefixCodedShift;
+
+/**
+ * Decomposes a given {@link GeoPointMultiTermQuery} into a set of terms that represent the query criteria using
+ * {@link GeoPointField.TermEncoding#PREFIX} method defined by
+ * {@link GeoPointField}. The terms are then enumerated by the
+ * {@link GeoPointTermQueryConstantScoreWrapper} and all docs whose GeoPoint fields match the prefix terms or pass
+ * the {@link GeoPointMultiTermQuery.CellComparator#postFilter} criteria are returned in the
+ * resulting DocIdSet.
+ *
+ * @lucene.experimental
+ */
+final class GeoPointPrefixTermsEnum extends GeoPointTermsEnum {
+ private final long start;
+
+ private short shift;
+
+ // current range as long
+ private long currStart;
+ private long currEnd;
+
+ private final Range nextRange = new Range(-1, shift, true);
+
+ private boolean hasNext = false;
+
+ private boolean withinOnly = false;
+ private long lastWithin;
+
+ public GeoPointPrefixTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
+ super(tenum, query);
+ this.start = mortonHash(query.minLon, query.minLat);
+ this.currentRange = new Range(0, shift, true);
+ // start shift at maxShift value (from computeMaxShift)
+ this.shift = maxShift;
+ final long mask = (1L << shift) - 1;
+ this.currStart = start & ~mask;
+ this.currEnd = currStart | mask;
+ }
+
+ private boolean within(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ return relationImpl.cellWithin(minLon, minLat, maxLon, maxLat);
+ }
+
+ private boolean boundary(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+ return shift == maxShift && relationImpl.cellIntersectsShape(minLon, minLat, maxLon, maxLat);
+ }
+
+ private boolean nextWithin() {
+ if (withinOnly == false) {
+ return false;
+ }
+ currStart += (1L << shift);
+ setNextRange(false);
+ currentRange.set(nextRange);
+ hasNext = true;
+
+ withinOnly = lastWithin != currStart;
+ if (withinOnly == false) advanceVariables();
+ return true;
+ }
+
+ private void nextRelation() {
+ double minLon = mortonUnhashLon(currStart);
+ double minLat = mortonUnhashLat(currStart);
+ double maxLon;
+ double maxLat;
+ boolean isWithin;
+ do {
+ maxLon = mortonUnhashLon(currEnd);
+ maxLat = mortonUnhashLat(currEnd);
+
+ // within or a boundary
+ if ((isWithin = within(minLon, minLat, maxLon, maxLat) == true) || boundary(minLon, minLat, maxLon, maxLat) == true) {
+ final int m;
+ if (isWithin == false || (m = shift % GeoPointField.PRECISION_STEP) == 0) {
+ setNextRange(isWithin == false);
+ advanceVariables();
+ break;
+ } else if (shift < 54) {
+ withinOnly = true;
+ shift = (short)(shift - m);
+ lastWithin = currEnd & ~((1L << shift) - 1);
+ setNextRange(false);
+ break;
+ }
+ }
+
+ // within cell but not at a depth factor of PRECISION_STEP
+ if (isWithin == true || (relationImpl.cellIntersectsMBR(minLon, minLat, maxLon , maxLat) == true && shift != maxShift)) {
+ // descend: currStart need not change since shift handles end of range
+ currEnd = currStart | (1L<<--shift) - 1;
+ } else {
+ advanceVariables();
+ minLon = mortonUnhashLon(currStart);
+ minLat = mortonUnhashLat(currStart);
+ }
+ } while(shift < 63);
+ }
+
+ private void setNextRange(final boolean boundary) {
+ nextRange.start = currStart;
+ nextRange.shift = shift;
+ nextRange.boundary = boundary;
+ }
+
+ private void advanceVariables() {
+ /** set next variables */
+ long shiftMask = 1L << shift;
+ // pop-up if shift bit is set
+ while ( (currStart & shiftMask) == shiftMask) {
+ shiftMask = 1L << ++shift;
+ }
+ final long shiftMOne = shiftMask - 1;
+ currStart = currStart & ~shiftMOne | shiftMask;
+ currEnd = currStart | shiftMOne;
+ }
+
+ @Override
+ protected final BytesRef peek() {
+ nextRange.fillBytesRef(nextSubRangeBRB);
+ return super.peek();
+ }
+
+ protected void seek(long term, short res) {
+ if (term < currStart && res < maxShift) {
+ throw new IllegalArgumentException("trying to seek backwards");
+ } else if (term == currStart) {
+ return;
+ }
+ shift = res;
+ currStart = term;
+ currEnd = currStart | ((1L<<shift)-1);
+ withinOnly = false;
+ }
+
+ @Override
+ protected void nextRange() {
+ hasNext = false;
+ super.nextRange();
+ }
+
+ @Override
+ protected final boolean hasNext() {
+ if (hasNext == true || nextWithin()) {
+ return true;
+ }
+ nextRelation();
+ if (currentRange.compareTo(nextRange) != 0) {
+ currentRange.set(nextRange);
+ return (hasNext = true);
+ }
+ return false;
+ }
+
+ @Override
+ protected final BytesRef nextSeekTerm(BytesRef term) {
+ while (hasNext()) {
+ nextRange();
+ if (term == null) {
+ return currentCell;
+ }
+
+ final int comparison = term.compareTo(currentCell);
+ if (comparison > 0) {
+ seek(GeoEncodingUtils.prefixCodedToGeoCoded(term), (short)(64-GeoEncodingUtils.getPrefixCodedShift(term)));
+ continue;
+ }
+ return currentCell;
+ }
+
+ // no more sub-range enums available
+ return null;
+ }
+
+ @Override
+ protected AcceptStatus accept(BytesRef term) {
+ // range < term or range is null
+ while (currentCell == null || term.compareTo(currentCell) > 0) {
+ // no more ranges, be gone
+ if (hasNext() == false) {
+ return AcceptStatus.END;
+ }
+
+ // peek next range, if the range > term then seek
+ final int peekCompare = term.compareTo(peek());
+ if (peekCompare < 0) {
+ return AcceptStatus.NO_AND_SEEK;
+ } else if (peekCompare > 0) {
+ seek(prefixCodedToGeoCoded(term), (short)(64 - getPrefixCodedShift(term)));
+ }
+ nextRange();
+ }
+ return AcceptStatus.YES;
+ }
+
+ protected final class Range extends BaseRange {
+ public Range(final long start, final short res, final boolean boundary) {
+ super(start, res, boundary);
+ }
+
+ @Override
+ protected void fillBytesRef(BytesRefBuilder result) {
+ assert result != null;
+ geoCodedToPrefixCoded(start, shift, result);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermQuery.java
deleted file mode 100644
index 894a1e9..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermQuery.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.spatial.search;
-
-import java.io.IOException;
-
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.Terms;
-import org.apache.lucene.index.TermsEnum;
-import org.apache.lucene.search.MultiTermQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.spatial.util.GeoUtils;
-import org.apache.lucene.util.AttributeSource;
-
-/**
- * TermQuery for GeoPointField for overriding {@link org.apache.lucene.search.MultiTermQuery} methods specific to
- * Geospatial operations
- *
- * @lucene.experimental
- */
-abstract class GeoPointTermQuery extends MultiTermQuery {
- // simple bounding box optimization - no objects used to avoid dependencies
- /** minimum longitude value (in degrees) */
- protected final double minLon;
- /** minimum latitude value (in degrees) */
- protected final double minLat;
- /** maximum longitude value (in degrees) */
- protected final double maxLon;
- /** maximum latitude value (in degrees) */
- protected final double maxLat;
-
- /**
- * Constructs a query matching terms that cannot be represented with a single
- * Term.
- */
- public GeoPointTermQuery(String field, final double minLon, final double minLat, final double maxLon, final double maxLat) {
- super(field);
-
- if (GeoUtils.isValidLon(minLon) == false) {
- throw new IllegalArgumentException("invalid minLon " + minLon);
- }
- if (GeoUtils.isValidLon(maxLon) == false) {
- throw new IllegalArgumentException("invalid maxLon " + maxLon);
- }
- if (GeoUtils.isValidLat(minLat) == false) {
- throw new IllegalArgumentException("invalid minLat " + minLat);
- }
- if (GeoUtils.isValidLat(maxLat) == false) {
- throw new IllegalArgumentException("invalid maxLat " + maxLat);
- }
- this.minLon = minLon;
- this.minLat = minLat;
- this.maxLon = maxLon;
- this.maxLat = maxLat;
-
- this.rewriteMethod = GEO_CONSTANT_SCORE_REWRITE;
- }
-
- private static final RewriteMethod GEO_CONSTANT_SCORE_REWRITE = new RewriteMethod() {
- @Override
- public Query rewrite(IndexReader reader, MultiTermQuery query) {
- return new GeoPointTermQueryConstantScoreWrapper<>((GeoPointTermQuery)query);
- }
- };
-
- /** override package protected method */
- @Override
- protected abstract TermsEnum getTermsEnum(final Terms terms, AttributeSource atts) throws IOException;
-
- /** check if this instance equals another instance */
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- if (!super.equals(o)) return false;
-
- GeoPointTermQuery that = (GeoPointTermQuery) o;
-
- if (Double.compare(that.minLon, minLon) != 0) return false;
- if (Double.compare(that.minLat, minLat) != 0) return false;
- if (Double.compare(that.maxLon, maxLon) != 0) return false;
- return Double.compare(that.maxLat, maxLat) == 0;
- }
-
- /** compute hashcode */
- @Override
- public int hashCode() {
- int result = super.hashCode();
- long temp;
- temp = Double.doubleToLongBits(minLon);
- result = 31 * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(minLat);
- result = 31 * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(maxLon);
- result = 31 * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(maxLat);
- result = 31 * result + (int) (temp ^ (temp >>> 32));
- return result;
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/74a08c08/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermQueryConstantScoreWrapper.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermQueryConstantScoreWrapper.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermQueryConstantScoreWrapper.java
index 8176aec..1097add 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermQueryConstantScoreWrapper.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointTermQueryConstantScoreWrapper.java
@@ -33,15 +33,18 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Weight;
import org.apache.lucene.util.DocIdSetBuilder;
-import org.apache.lucene.spatial.util.GeoUtils;
+
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.mortonUnhashLat;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.mortonUnhashLon;
+
/**
- * Custom ConstantScoreWrapper for {@code GeoPointTermQuery} that cuts over to DocValues
+ * Custom ConstantScoreWrapper for {@code GeoPointMultiTermQuery} that cuts over to DocValues
* for post filtering boundary ranges. Multi-valued GeoPoint documents are supported.
*
* @lucene.experimental
*/
-final class GeoPointTermQueryConstantScoreWrapper <Q extends GeoPointTermQuery> extends Query {
+final class GeoPointTermQueryConstantScoreWrapper <Q extends GeoPointMultiTermQuery> extends Query {
protected final Q query;
protected GeoPointTermQueryConstantScoreWrapper(Q query) {
@@ -95,7 +98,7 @@ final class GeoPointTermQueryConstantScoreWrapper <Q extends GeoPointTermQuery>
sdv.setDocument(docId);
for (int i=0; i<sdv.count(); ++i) {
hash = sdv.valueAt(i);
- if (termsEnum.postFilter(GeoUtils.mortonUnhashLon(hash), GeoUtils.mortonUnhashLat(hash))) {
+ if (termsEnum.postFilter(mortonUnhashLon(hash), mortonUnhashLat(hash))) {
builder.add(docId);
break;
}
[5/5] lucene-solr git commit: add super.rewrite to
GeoPointInPolygonQuery.rewrite, fix invalid javadoc reference
Posted by nk...@apache.org.
add super.rewrite to GeoPointInPolygonQuery.rewrite, fix invalid javadoc reference
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/cc052491
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/cc052491
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/cc052491
Branch: refs/heads/branch_5x
Commit: cc052491cad5063b87fc61189c48b1d7b6656ce3
Parents: 331c03d
Author: nknize <nk...@apache.org>
Authored: Sat Feb 6 22:13:00 2016 -0600
Committer: nknize <nk...@apache.org>
Committed: Sat Feb 6 22:26:38 2016 -0600
----------------------------------------------------------------------
.../apache/lucene/spatial/document/GeoPointTokenStream.java | 4 ++--
.../apache/lucene/spatial/search/GeoPointInPolygonQuery.java | 6 +++++-
2 files changed, 7 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/cc052491/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
index ff97fee..1d19167 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
@@ -40,7 +40,7 @@ import static org.apache.lucene.spatial.document.GeoPointField.PRECISION_STEP;
* <p><i>NOTE: This is used as the default encoding unless
* {@code GeoPointField.setNumericType(FieldType.LegacyNumericType.LONG)} is set</i></p>
*
- * This class is similar to {@link org.apache.lucene.analysis.LegacyNumericTokenStream} but encodes terms up to a
+ * This class is similar to {@link org.apache.lucene.analysis.NumericTokenStream} but encodes terms up to a
* a maximum of {@link #MAX_SHIFT} using a fixed precision step defined by
* {@link GeoPointField#PRECISION_STEP}. This yields a total of 4 terms per GeoPoint
* each consisting of 5 bytes (4 prefix bytes + 1 precision byte).
@@ -49,7 +49,7 @@ import static org.apache.lucene.spatial.document.GeoPointField.PRECISION_STEP;
* {@link GeoPointField#PREFIX_TYPE_STORED}</p>
*
* <p>If prefix terms are used then the default GeoPoint query constructors may be used, but if
- * {@link org.apache.lucene.analysis.LegacyNumericTokenStream} is used, then be sure to pass
+ * {@link org.apache.lucene.analysis.NumericTokenStream} is used, then be sure to pass
* {@link GeoPointField.TermEncoding#NUMERIC} to all GeoPointQuery constructors</p>
*
* Here's an example usage:
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/cc052491/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
index eb8c50c..960cc66 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
@@ -16,6 +16,7 @@
*/
package org.apache.lucene.spatial.search;
+import java.io.IOException;
import java.util.Arrays;
import org.apache.lucene.index.IndexReader;
@@ -84,7 +85,10 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQuery {
/** throw exception if trying to change rewrite method */
@Override
- public Query rewrite(IndexReader reader) {
+ public Query rewrite(IndexReader reader) throws IOException {
+ if (getBoost() != 1f) {
+ return super.rewrite(reader);
+ }
return new GeoPointInPolygonQueryImpl(field, termEncoding, this, this.minLon, this.minLat, this.maxLon, this.maxLat);
}
[3/5] lucene-solr git commit: update CHANGES.txt. Cleanup javadoc,
still a broken link from package private inherited class
Posted by nk...@apache.org.
update CHANGES.txt. Cleanup javadoc, still a broken link from package private inherited class
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/f63f1521
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/f63f1521
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/f63f1521
Branch: refs/heads/branch_5x
Commit: f63f1521c8b58d21f36a9520cddbb8fa8e40bfc8
Parents: 74a08c0
Author: nknize <nk...@apache.org>
Authored: Fri Feb 5 23:56:19 2016 -0600
Committer: nknize <nk...@apache.org>
Committed: Sat Feb 6 22:09:40 2016 -0600
----------------------------------------------------------------------
lucene/CHANGES.txt | 4 ++++
.../apache/lucene/spatial/document/GeoPointField.java | 2 +-
.../lucene/spatial/document/GeoPointTokenStream.java | 2 +-
.../lucene/spatial/search/GeoPointInBBoxQuery.java | 2 +-
.../lucene/spatial/search/GeoPointInPolygonQuery.java | 12 ++----------
.../lucene/spatial/search/GeoPointNumericTermsEnum.java | 2 +-
.../lucene/spatial/search/GeoPointPrefixTermsEnum.java | 2 +-
7 files changed, 11 insertions(+), 15 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f63f1521/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index d30fe25..5fcce6d 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -66,6 +66,10 @@ API Changes
Optimizations
+* LUCENE-6930: Decouple GeoPointField from NumericType by using a custom
+ and efficient GeoPointTokenStream and TermEnum designed for GeoPoint prefix
+ terms. (Nick Knize)
+
* LUCENE-6951: Improve GeoPointInPolygonQuery using point orientation based
line crossing algorithm, and adding result for multi-value docs when least
1 point satisfies polygon criteria. (Nick Knize)
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f63f1521/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java
index 3cdd6ab..dcc7c91 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java
@@ -54,7 +54,7 @@ public final class GeoPointField extends Field {
public enum TermEncoding {
/**
* encodes prefix terms only resulting in a small index and faster queries - use with
- * {@link GeoPointTokenStream}
+ * {@code GeoPointTokenStream}
*/
PREFIX,
/**
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f63f1521/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
index e22d446..ff97fee 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
@@ -85,7 +85,7 @@ final class GeoPointTokenStream extends TokenStream {
* <code>precisionStep</code> using the given
* {@link org.apache.lucene.util.AttributeFactory}.
* The stream is not yet initialized,
- * before using set a value using the various set<em>???</em>Value() methods.
+ * before using set a value using the various setGeoCode method.
*/
public GeoPointTokenStream() {
super(new GeoPointAttributeFactory(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY));
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f63f1521/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java
index b7fcf70..9a6a828 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java
@@ -34,7 +34,7 @@ import org.apache.lucene.spatial.util.GeoUtils;
* passing this initial filter are passed to a final check that verifies whether
* the decoded lat/lon falls within (or on the boundary) of the query bounding box.
* The value comparisons are subject to a precision tolerance defined in
- * {@value org.apache.lucene.spatial.util.GeoUtils#TOLERANCE}
+ * {@value org.apache.lucene.spatial.util.GeoEncodingUtils#TOLERANCE}
*
* NOTES:
* 1. All latitude/longitude values must be in decimal degrees.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f63f1521/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
index a9be21e..a3cc061 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
@@ -16,12 +16,8 @@
*/
package org.apache.lucene.spatial.search;
-import java.io.IOException;
import java.util.Arrays;
-import org.apache.lucene.index.Terms;
-import org.apache.lucene.index.TermsEnum;
-import org.apache.lucene.util.AttributeSource;
import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
import org.apache.lucene.spatial.util.GeoEncodingUtils;
import org.apache.lucene.spatial.util.GeoRect;
@@ -36,11 +32,7 @@ import org.apache.lucene.spatial.util.GeoUtils;
* to a secondary filter that verifies whether the decoded lat/lon point falls within
* (or on the boundary) of the bounding box query. Finally, the remaining candidate
* term is passed to the final point in polygon check. All value comparisons are subject
-<<<<<<< HEAD:lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
- * to the same precision tolerance defined in {@value org.apache.lucene.spatial.util.GeoUtils#TOLERANCE}
-=======
* to the same precision tolerance defined in {@value GeoEncodingUtils#TOLERANCE}
->>>>>>> LUCENE-6930: Decouples GeoPointField from NumericType by using a custom GeoPointTokenStream and TermEnum designed for GeoPoint prefix terms:lucene/sandbox/src/java/org/apache/lucene/search/GeoPointInPolygonQuery.java
*
* <p>NOTES:
* 1. The polygon coordinates need to be in either clockwise or counter-clockwise order.
@@ -101,8 +93,8 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQueryImpl {
}
/**
- * Custom {@link org.apache.lucene.index.TermsEnum} that computes morton hash ranges based on the defined edges of
- * the provided polygon.
+ * Custom {@code org.apache.lucene.spatial.search.GeoPointMultiTermQuery.CellComparator} that computes morton hash
+ * ranges based on the defined edges of the provided polygon.
*/
private final class GeoPolygonCellComparator extends CellComparator {
GeoPolygonCellComparator(GeoPointMultiTermQuery query) {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f63f1521/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointNumericTermsEnum.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointNumericTermsEnum.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointNumericTermsEnum.java
index 566d917..9972125 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointNumericTermsEnum.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointNumericTermsEnum.java
@@ -30,7 +30,7 @@ import org.apache.lucene.util.NumericUtils;
/**
* Decomposes a given {@link GeoPointMultiTermQuery} into a set of terms that represent the query criteria using
- * {@link import org.apache.lucene.document.GeoPointTokenStream.TermEncoding#NUMERIC} method defined by
+ * {@link org.apache.lucene.spatial.document.GeoPointField.TermEncoding#NUMERIC} method defined by
* {@link org.apache.lucene.analysis.NumericTokenStream}. The terms are then enumerated by the
* {@link GeoPointTermQueryConstantScoreWrapper} and all docs whose GeoPoint fields match the prefix terms or
* pass the {@link GeoPointMultiTermQuery.CellComparator#postFilter} criteria are returned in the resulting DocIdSet.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f63f1521/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointPrefixTermsEnum.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointPrefixTermsEnum.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointPrefixTermsEnum.java
index 239e959..d08cd98 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointPrefixTermsEnum.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointPrefixTermsEnum.java
@@ -32,7 +32,7 @@ import static org.apache.lucene.spatial.util.GeoEncodingUtils.getPrefixCodedShif
/**
* Decomposes a given {@link GeoPointMultiTermQuery} into a set of terms that represent the query criteria using
- * {@link GeoPointField.TermEncoding#PREFIX} method defined by
+ * {@link org.apache.lucene.spatial.document.GeoPointField.TermEncoding#PREFIX} method defined by
* {@link GeoPointField}. The terms are then enumerated by the
* {@link GeoPointTermQueryConstantScoreWrapper} and all docs whose GeoPoint fields match the prefix terms or pass
* the {@link GeoPointMultiTermQuery.CellComparator#postFilter} criteria are returned in the