You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ds...@apache.org on 2016/06/10 17:44:36 UTC

lucene-solr:branch_6_1: LUCENE-7291: Fix spatial HeatmapFacetCounter bug with dateline and large non-point shapes (cherry picked from commit 7520d79)

Repository: lucene-solr
Updated Branches:
  refs/heads/branch_6_1 1bbac6bbd -> 6372c0b40


LUCENE-7291: Fix spatial HeatmapFacetCounter bug with dateline and large non-point shapes
(cherry picked from commit 7520d79)


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/6372c0b4
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/6372c0b4
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/6372c0b4

Branch: refs/heads/branch_6_1
Commit: 6372c0b4042ec2c8d94e50e5c2b9c1df469414e2
Parents: 1bbac6b
Author: David Smiley <ds...@apache.org>
Authored: Fri Jun 10 13:36:07 2016 -0400
Committer: David Smiley <ds...@apache.org>
Committed: Fri Jun 10 13:43:49 2016 -0400

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |  4 +++
 .../spatial/prefix/HeatmapFacetCounter.java     | 34 +++++++++++---------
 .../spatial/prefix/HeatmapFacetCounterTest.java | 27 ++++++++++------
 3 files changed, 40 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6372c0b4/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 7b0f069..1b5f15f 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -111,6 +111,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,

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6372c0b4/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/HeatmapFacetCounter.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/HeatmapFacetCounter.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/HeatmapFacetCounter.java
index adee2be..c3c98f7 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/HeatmapFacetCounter.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/HeatmapFacetCounter.java
@@ -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;
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6372c0b4/lucene/spatial-extras/src/test/org/apache/lucene/spatial/prefix/HeatmapFacetCounterTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/prefix/HeatmapFacetCounterTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/prefix/HeatmapFacetCounterTest.java
index 2de18cc..a38f5b6 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/prefix/HeatmapFacetCounterTest.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/prefix/HeatmapFacetCounterTest.java
@@ -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;
@@ -83,6 +83,15 @@ public class HeatmapFacetCounterTest extends StrategyTestCase {
   }
 
   @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,
     final SpatialContextFactory spatialContextFactory = new SpatialContextFactory();