Jelajahi Sumber

CentroidCalculator does not return negative summation weights (#135176) (#135189)

Small polygons with holes can compute negative weights due to rounding errors and break 
serialization for those geometries.
Ignacio Vera 2 minggu lalu
induk
melakukan
61e8bfc426

+ 6 - 0
docs/changelog/135176.yaml

@@ -0,0 +1,6 @@
+pr: 135176
+summary: '`CentroidCalculator` does not return negative summation weights'
+area: Geo
+type: bug
+issues:
+ - 131861

+ 8 - 1
server/src/main/java/org/elasticsearch/lucene/spatial/CentroidCalculator.java

@@ -63,7 +63,14 @@ public class CentroidCalculator {
      * @return the sum of all the weighted coordinates summed in the calculator
      */
     public double sumWeight() {
-        return visitor.compSumWeight.value();
+        double sumWeight = visitor.compSumWeight.value();
+        if (sumWeight < 0) {
+            // we can get negative values due to rounding errors in degenerated polygons. The proper fix would be to compute
+            // the centroid using the tessellation algorithm. For the time being we just return 0.
+            return 0d;
+        }
+
+        return sumWeight;
     }
 
     /**

+ 31 - 0
server/src/test/java/org/elasticsearch/lucene/spatial/CentroidCalculatorTests.java

@@ -37,6 +37,7 @@ import static org.elasticsearch.lucene.spatial.DimensionalShapeType.POLYGON;
 import static org.hamcrest.Matchers.closeTo;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
 
 public abstract class CentroidCalculatorTests extends ESTestCase {
     private static final double DELTA = 0.000000001;
@@ -398,6 +399,36 @@ public abstract class CentroidCalculatorTests extends ESTestCase {
         }
     }
 
+    public void testDegeneratedPolygon() {
+        LinearRing outer = new LinearRing(
+            new double[] {
+                144.9738739160867,
+                144.97387390626216,
+                144.97387394861903,
+                144.97387410671985,
+                144.97387431344814,
+                144.97387422754971,
+                144.97387403679602,
+                144.9738739160867 },
+            new double[] {
+                -37.812764320048906,
+                -37.81276430546101,
+                -37.81276433502755,
+                -37.81276449365169,
+                -37.81276470177617,
+                -37.8127646444251,
+                -37.812764516780305,
+                -37.812764320048906 }
+        );
+        LinearRing inner = new LinearRing(
+            new double[] { 144.9738739160867, 144.97387396264085, 144.97387400134224, 144.97387405619358, 144.9738739160867 },
+            new double[] { -37.812764320048906, -37.81276437348886, -37.81276441404389, -37.81276447205519, -37.812764320048906 }
+        );
+        CentroidCalculator polygonCalculator = new CentroidCalculator();
+        polygonCalculator.add(new Polygon(outer, List.of(inner)));
+        assertThat(polygonCalculator.sumWeight(), greaterThanOrEqualTo(0d));
+    }
+
     private Matcher<CentroidCalculator> matchesCentroid(Point point, double weight) {
         return new CentroidMatcher(point.getX(), point.getY(), weight, 1.0);
     }