mirror of https://github.com/apache/lucene.git
LUCENE-7291: Fix spatial HeatmapFacetCounter bug with dateline and large non-point shapes
This commit is contained in:
parent
e4db256d3f
commit
b33d7176aa
|
@ -143,6 +143,10 @@ Bug Fixes
|
|||
|
||||
* LUCENE-7286: Added support for highlighting SynonymQuery. (Adrien Grand)
|
||||
|
||||
* LUCENE-7291: Spatial heatmap faceting could mis-count when the heatmap crosses the
|
||||
dateline and indexed non-point shapes are much bigger than the heatmap region.
|
||||
(David Smiley)
|
||||
|
||||
Other
|
||||
|
||||
* LUCENE-7295: TermAutomatonQuery.hashCode calculates Automaton.toDot().hash,
|
||||
|
|
|
@ -20,17 +20,17 @@ import java.io.IOException;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.SpatialRelation;
|
||||
import org.apache.lucene.index.IndexReaderContext;
|
||||
import org.apache.lucene.spatial.prefix.tree.Cell;
|
||||
import org.apache.lucene.spatial.prefix.tree.CellIterator;
|
||||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
import org.apache.lucene.util.ArrayUtil;
|
||||
import org.apache.lucene.util.Bits;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.SpatialRelation;
|
||||
|
||||
/**
|
||||
* Computes spatial facets in two dimensions as a grid of numbers. The data is often visualized as a so-called
|
||||
|
@ -204,8 +204,9 @@ public class HeatmapFacetCounter {
|
|||
|
||||
int[] pair = new int[2];//output of intersectInterval
|
||||
for (Map.Entry<Rectangle, Integer> entry : ancestors.entrySet()) {
|
||||
Rectangle rect = entry.getKey();
|
||||
Rectangle rect = entry.getKey(); // from a cell (thus doesn't cross DL)
|
||||
final int count = entry.getValue();
|
||||
|
||||
//note: we approach this in a way that eliminates int overflow/underflow (think huge cell, tiny heatmap)
|
||||
intersectInterval(heatMinY, heatMaxY, cellHeight, rows, rect.getMinY(), rect.getMaxY(), pair);
|
||||
final int startRow = pair[0];
|
||||
|
@ -218,32 +219,33 @@ public class HeatmapFacetCounter {
|
|||
incrementRange(heatmap, startCol, endCol, startRow, endRow, count);
|
||||
|
||||
} else {
|
||||
// note: the cell rect might intersect 2 disjoint parts of the heatmap, so we do the left & right separately
|
||||
final int leftColumns = (int) Math.round((180 - heatMinX) / cellWidth);
|
||||
final int rightColumns = heatmap.columns - leftColumns;
|
||||
//left half of dateline:
|
||||
if (rect.getMaxX() >= heatMinX) {
|
||||
final int leftColumns = (int) Math.round((180 - heatMinX) / cellWidth) + 1;
|
||||
if (rect.getMaxX() > heatMinX) {
|
||||
intersectInterval(heatMinX, 180, cellWidth, leftColumns, rect.getMinX(), rect.getMaxX(), pair);
|
||||
final int startCol = pair[0];
|
||||
final int endCol = pair[1];
|
||||
incrementRange(heatmap, startCol, endCol, startRow, endRow, count);
|
||||
}
|
||||
//right half of dateline
|
||||
if (rect.getMinY() <= heatMaxX) {
|
||||
final int rightColumns = (int) Math.round(heatMaxX / cellWidth) + 1;
|
||||
intersectInterval(0, heatMaxX, cellWidth, rightColumns, rect.getMinX(), rect.getMaxX(), pair);
|
||||
final int startCol = pair[0];
|
||||
final int endCol = pair[1];
|
||||
if (rect.getMinX() < heatMaxX) {
|
||||
intersectInterval(-180, heatMaxX, cellWidth, rightColumns, rect.getMinX(), rect.getMaxX(), pair);
|
||||
final int startCol = pair[0] + leftColumns;
|
||||
final int endCol = pair[1] + leftColumns;
|
||||
incrementRange(heatmap, startCol, endCol, startRow, endRow, count);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return heatmap;
|
||||
}
|
||||
|
||||
private static void intersectInterval(double heatMin, double heatMax, double heatCellLen, int heatLen,
|
||||
private static void intersectInterval(double heatMin, double heatMax, double heatCellLen, int numCells,
|
||||
double cellMin, double cellMax,
|
||||
int[] out) {
|
||||
assert heatMin < heatMax && cellMin < cellMax;
|
||||
//precondition: we know there's an intersection
|
||||
if (heatMin >= cellMin) {
|
||||
out[0] = 0;
|
||||
|
@ -251,7 +253,7 @@ public class HeatmapFacetCounter {
|
|||
out[0] = (int) Math.round((cellMin - heatMin) / heatCellLen);
|
||||
}
|
||||
if (heatMax <= cellMax) {
|
||||
out[1] = heatLen - 1;
|
||||
out[1] = numCells - 1;
|
||||
} else {
|
||||
out[1] = (int) Math.round((cellMax - heatMin) / heatCellLen) - 1;
|
||||
}
|
||||
|
|
|
@ -21,15 +21,6 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.context.SpatialContextFactory;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Circle;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.SpatialRelation;
|
||||
import org.locationtech.spatial4j.shape.impl.RectangleImpl;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TotalHitCountCollector;
|
||||
import org.apache.lucene.spatial.StrategyTestCase;
|
||||
|
@ -39,6 +30,15 @@ import org.apache.lucene.util.Bits;
|
|||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.context.SpatialContextFactory;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Circle;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.SpatialRelation;
|
||||
import org.locationtech.spatial4j.shape.impl.RectangleImpl;
|
||||
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.atMost;
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomIntBetween;
|
||||
|
@ -82,6 +82,15 @@ public class HeatmapFacetCounterTest extends StrategyTestCase {
|
|||
// add specific tests if we find a bug.
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLucene7291Dateline() throws IOException {
|
||||
grid = new QuadPrefixTree(ctx, 2); // only 2, and we wind up with some big leaf cells
|
||||
strategy = new RecursivePrefixTreeStrategy(grid, getTestClass().getSimpleName());
|
||||
adoc("0", ctx.makeRectangle(-102, -83, 43, 52));
|
||||
commit();
|
||||
validateHeatmapResultLoop(ctx.makeRectangle(179, -179, 62, 63), 2, 100);// HM crosses dateline
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryCircle() throws IOException {
|
||||
//overwrite setUp; non-geo bounds is more straight-forward; otherwise 88,88 would actually be practically north,
|
||||
|
|
Loading…
Reference in New Issue