From 6b0dd1b8f63fd8571cf20eaefa50080487daa0e8 Mon Sep 17 00:00:00 2001 From: Ignacio Vera Date: Mon, 22 Sep 2025 14:20:16 +0200 Subject: [PATCH] CentroidCalculator does not return negative summation weights (#135176) Small polygons with holes can compute negative weights due to rounding errors and break serialization for those geometries. --- docs/changelog/135176.yaml | 6 ++++ .../lucene/spatial/CentroidCalculator.java | 9 +++++- .../spatial/CentroidCalculatorTests.java | 31 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/135176.yaml diff --git a/docs/changelog/135176.yaml b/docs/changelog/135176.yaml new file mode 100644 index 0000000000000..c86487ab999c8 --- /dev/null +++ b/docs/changelog/135176.yaml @@ -0,0 +1,6 @@ +pr: 135176 +summary: '`CentroidCalculator` does not return negative summation weights' +area: Geo +type: bug +issues: + - 131861 diff --git a/server/src/main/java/org/elasticsearch/lucene/spatial/CentroidCalculator.java b/server/src/main/java/org/elasticsearch/lucene/spatial/CentroidCalculator.java index 6ce036125e871..38b6e347d1903 100644 --- a/server/src/main/java/org/elasticsearch/lucene/spatial/CentroidCalculator.java +++ b/server/src/main/java/org/elasticsearch/lucene/spatial/CentroidCalculator.java @@ -63,7 +63,14 @@ public double getY() { * @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; } /** diff --git a/server/src/test/java/org/elasticsearch/lucene/spatial/CentroidCalculatorTests.java b/server/src/test/java/org/elasticsearch/lucene/spatial/CentroidCalculatorTests.java index 8ba7fe5835880..65331e7544d68 100644 --- a/server/src/test/java/org/elasticsearch/lucene/spatial/CentroidCalculatorTests.java +++ b/server/src/test/java/org/elasticsearch/lucene/spatial/CentroidCalculatorTests.java @@ -37,6 +37,7 @@ 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 void testAddDifferentDimensionalType() { } } + 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 matchesCentroid(Point point, double weight) { return new CentroidMatcher(point.getX(), point.getY(), weight, 1.0); }