Skip to content

Commit d746eee

Browse files
authored
Simplify creation of CentroidCalculator objects (#64619) (#64686)
Creation of those objects is responsibility now of BinaryGeoShapeDocValuesField.
1 parent c8bf420 commit d746eee

File tree

13 files changed

+222
-213
lines changed

13 files changed

+222
-213
lines changed

x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/CentroidCalculator.java

Lines changed: 55 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
package org.elasticsearch.xpack.spatial.index.fielddata;
88

9-
import org.elasticsearch.common.geo.GeoUtils;
109
import org.elasticsearch.geometry.Circle;
1110
import org.elasticsearch.geometry.Geometry;
1211
import org.elasticsearch.geometry.GeometryCollection;
@@ -27,106 +26,62 @@
2726
* as the centroid of a shape.
2827
*/
2928
public class CentroidCalculator {
30-
CompensatedSum compSumX;
31-
CompensatedSum compSumY;
32-
CompensatedSum compSumWeight;
33-
private CentroidCalculatorVisitor visitor;
34-
private DimensionalShapeType dimensionalShapeType;
35-
36-
public CentroidCalculator(Geometry geometry) {
37-
this.compSumX = new CompensatedSum(0, 0);
38-
this.compSumY = new CompensatedSum(0, 0);
39-
this.compSumWeight = new CompensatedSum(0, 0);
40-
this.dimensionalShapeType = null;
41-
this.visitor = new CentroidCalculatorVisitor(this);
42-
geometry.visit(visitor);
43-
this.dimensionalShapeType = visitor.calculator.dimensionalShapeType;
44-
}
4529

46-
/**
47-
* adds a single coordinate to the running sum and count of coordinates
48-
* for centroid calculation
49-
* @param x the x-coordinate of the point
50-
* @param y the y-coordinate of the point
51-
* @param weight the associated weight of the coordinate
52-
*/
53-
private void addCoordinate(double x, double y, double weight, DimensionalShapeType dimensionalShapeType) {
54-
// x and y can be infinite due to really small areas and rounding problems
55-
if (Double.isFinite(x) && Double.isFinite(y)) {
56-
if (this.dimensionalShapeType == null || this.dimensionalShapeType == dimensionalShapeType) {
57-
compSumX.add(x * weight);
58-
compSumY.add(y * weight);
59-
compSumWeight.add(weight);
60-
this.dimensionalShapeType = dimensionalShapeType;
61-
} else if (dimensionalShapeType.compareTo(this.dimensionalShapeType) > 0) {
62-
// reset counters
63-
compSumX.reset(x * weight, 0);
64-
compSumY.reset(y * weight, 0);
65-
compSumWeight.reset(weight, 0);
66-
this.dimensionalShapeType = dimensionalShapeType;
67-
}
68-
}
30+
private final CentroidCalculatorVisitor visitor;
31+
32+
public CentroidCalculator() {
33+
this.visitor = new CentroidCalculatorVisitor();
6934
}
7035

7136
/**
72-
* Adjusts the existing calculator to add the running sum and count
73-
* from another {@link CentroidCalculator}. This is used to keep
74-
* a running count of points from different sub-shapes of a single
75-
* geo-shape field
37+
* Add a geometry to the calculator
7638
*
77-
* @param otherCalculator the other centroid calculator to add from
39+
* @param geometry the geometry to add
7840
*/
79-
public void addFrom(CentroidCalculator otherCalculator) {
80-
int compared = dimensionalShapeType.compareTo(otherCalculator.dimensionalShapeType);
81-
if (compared < 0) {
82-
dimensionalShapeType = otherCalculator.dimensionalShapeType;
83-
this.compSumX = otherCalculator.compSumX;
84-
this.compSumY = otherCalculator.compSumY;
85-
this.compSumWeight = otherCalculator.compSumWeight;
86-
87-
} else if (compared == 0) {
88-
this.compSumX.add(otherCalculator.compSumX.value());
89-
this.compSumY.add(otherCalculator.compSumY.value());
90-
this.compSumWeight.add(otherCalculator.compSumWeight.value());
91-
} // else (compared > 0) do not modify centroid calculation since otherCalculator is of lower dimension than this calculator
41+
public void add(Geometry geometry) {
42+
geometry.visit(visitor);
9243
}
9344

9445
/**
9546
* @return the x-coordinate centroid
9647
*/
9748
public double getX() {
98-
// normalization required due to floating point precision errors
99-
return GeoUtils.normalizeLon(compSumX.value() / compSumWeight.value());
49+
return visitor.compSumX.value() / visitor.compSumWeight.value();
10050
}
10151

10252
/**
10353
* @return the y-coordinate centroid
10454
*/
10555
public double getY() {
106-
// normalization required due to floating point precision errors
107-
return GeoUtils.normalizeLat(compSumY.value() / compSumWeight.value());
56+
return visitor.compSumY.value() / visitor.compSumWeight.value();
10857
}
10958

11059
/**
11160
* @return the sum of all the weighted coordinates summed in the calculator
11261
*/
11362
public double sumWeight() {
114-
return compSumWeight.value();
63+
return visitor.compSumWeight.value();
11564
}
11665

11766
/**
11867
* @return the highest dimensional shape type summed in the calculator
11968
*/
12069
public DimensionalShapeType getDimensionalShapeType() {
121-
return dimensionalShapeType;
70+
return visitor.dimensionalShapeType;
12271
}
12372

12473
private static class CentroidCalculatorVisitor implements GeometryVisitor<Void, IllegalArgumentException> {
12574

126-
private final CentroidCalculator calculator;
75+
final CompensatedSum compSumX;
76+
final CompensatedSum compSumY;
77+
final CompensatedSum compSumWeight;
78+
DimensionalShapeType dimensionalShapeType;
12779

128-
private CentroidCalculatorVisitor(CentroidCalculator calculator) {
129-
this.calculator = calculator;
80+
private CentroidCalculatorVisitor() {
81+
this.compSumX = new CompensatedSum(0, 0);
82+
this.compSumY = new CompensatedSum(0, 0);
83+
this.compSumWeight = new CompensatedSum(0, 0);
84+
this.dimensionalShapeType = DimensionalShapeType.POINT;
13085
}
13186

13287
@Override
@@ -144,7 +99,7 @@ public Void visit(GeometryCollection<?> collection) {
14499

145100
@Override
146101
public Void visit(Line line) {
147-
if (calculator.dimensionalShapeType != DimensionalShapeType.POLYGON) {
102+
if (dimensionalShapeType != DimensionalShapeType.POLYGON) {
148103
visitLine(line.length(), line::getX, line::getY);
149104
}
150105
return null;
@@ -158,7 +113,7 @@ public Void visit(LinearRing ring) {
158113

159114
@Override
160115
public Void visit(MultiLine multiLine) {
161-
if (calculator.getDimensionalShapeType() != DimensionalShapeType.POLYGON) {
116+
if (dimensionalShapeType != DimensionalShapeType.POLYGON) {
162117
for (Line line : multiLine) {
163118
visit(line);
164119
}
@@ -168,7 +123,7 @@ public Void visit(MultiLine multiLine) {
168123

169124
@Override
170125
public Void visit(MultiPoint multiPoint) {
171-
if (calculator.getDimensionalShapeType() == null || calculator.getDimensionalShapeType() == DimensionalShapeType.POINT) {
126+
if (dimensionalShapeType == DimensionalShapeType.POINT) {
172127
for (Point point : multiPoint) {
173128
visit(point);
174129
}
@@ -186,7 +141,7 @@ public Void visit(MultiPolygon multiPolygon) {
186141

187142
@Override
188143
public Void visit(Point point) {
189-
if (calculator.getDimensionalShapeType() == null || calculator.getDimensionalShapeType() == DimensionalShapeType.POINT) {
144+
if (dimensionalShapeType == DimensionalShapeType.POINT) {
190145
visitPoint(point.getX(), point.getY());
191146
}
192147
return null;
@@ -211,11 +166,11 @@ public Void visit(Polygon polygon) {
211166
sumWeight += w;
212167
}
213168

214-
if (sumWeight == 0 && calculator.dimensionalShapeType != DimensionalShapeType.POLYGON) {
169+
if (sumWeight == 0 && dimensionalShapeType != DimensionalShapeType.POLYGON) {
215170
visitLine(polygon.getPolygon().length(), polygon.getPolygon()::getX, polygon.getPolygon()::getY);
216171
} else {
217172
for (int i = 0; i < 1 + polygon.getNumberOfHoles(); i++) {
218-
calculator.addCoordinate(centroidX[i], centroidY[i], weight[i], DimensionalShapeType.POLYGON);
173+
addCoordinate(centroidX[i], centroidY[i], weight[i], DimensionalShapeType.POLYGON);
219174
}
220175
}
221176

@@ -230,7 +185,7 @@ public Void visit(Rectangle rectangle) {
230185
if (rectWeight != 0) {
231186
double sumX = rectangle.getMaxX() + rectangle.getMinX();
232187
double sumY = rectangle.getMaxY() + rectangle.getMinY();
233-
calculator.addCoordinate(sumX / 2, sumY / 2, rectWeight, DimensionalShapeType.POLYGON);
188+
addCoordinate(sumX / 2, sumY / 2, rectWeight, DimensionalShapeType.POLYGON);
234189
} else {
235190
// degenerated rectangle, transform to Line
236191
Line line = new Line(new double[]{rectangle.getMinX(), rectangle.getMaxX()},
@@ -240,9 +195,8 @@ public Void visit(Rectangle rectangle) {
240195
return null;
241196
}
242197

243-
244198
private void visitPoint(double x, double y) {
245-
calculator.addCoordinate(x, y, 1.0, DimensionalShapeType.POINT);
199+
addCoordinate(x, y, 1.0, DimensionalShapeType.POINT);
246200
}
247201

248202
private void visitLine(int length, CoordinateSupplier x, CoordinateSupplier y) {
@@ -258,7 +212,7 @@ private void visitLine(int length, CoordinateSupplier x, CoordinateSupplier y) {
258212
// degenerated line, it can be considered a point
259213
visitPoint(x.get(i), y.get(i));
260214
} else {
261-
calculator.addCoordinate(xAvg, yAvg, weight, DimensionalShapeType.LINE);
215+
addCoordinate(xAvg, yAvg, weight, DimensionalShapeType.LINE);
262216
}
263217
}
264218
}
@@ -289,6 +243,31 @@ private void visitLinearRing(int length, CoordinateSupplier x, CoordinateSupplie
289243
centroidY[idx] = sumY / (6 * totalRingArea);
290244
weight[idx] = sign * Math.abs(totalRingArea);
291245
}
246+
247+
/**
248+
* adds a single coordinate to the running sum and count of coordinates
249+
* for centroid calculation
250+
* @param x the x-coordinate of the point
251+
* @param y the y-coordinate of the point
252+
* @param weight the associated weight of the coordinate
253+
*/
254+
private void addCoordinate(double x, double y, double weight, DimensionalShapeType dimensionalShapeType) {
255+
// x and y can be infinite due to really small areas and rounding problems
256+
if (Double.isFinite(x) && Double.isFinite(y)) {
257+
if (this.dimensionalShapeType == dimensionalShapeType) {
258+
compSumX.add(x * weight);
259+
compSumY.add(y * weight);
260+
compSumWeight.add(weight);
261+
this.dimensionalShapeType = dimensionalShapeType;
262+
} else if (dimensionalShapeType.compareTo(this.dimensionalShapeType) > 0) {
263+
// reset counters
264+
compSumX.reset(x * weight, 0);
265+
compSumY.reset(y * weight, 0);
266+
compSumWeight.reset(weight, 0);
267+
this.dimensionalShapeType = dimensionalShapeType;
268+
}
269+
}
270+
}
292271
}
293272

294273
@FunctionalInterface

x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/CoordinateEncoder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,10 @@ public interface CoordinateEncoder {
2525

2626
/** decode Y value */
2727
double decodeY(int y);
28+
29+
/** normalize X value */
30+
double normalizeX(double x);
31+
32+
/** normalize Y value */
33+
double normalizeY(double y);
2834
}

x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/GeoShapeCoordinateEncoder.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.elasticsearch.xpack.spatial.index.fielddata;
88

99
import org.apache.lucene.geo.GeoEncodingUtils;
10+
import org.elasticsearch.common.geo.GeoUtils;
1011

1112
final class GeoShapeCoordinateEncoder implements CoordinateEncoder {
1213

@@ -41,4 +42,14 @@ public double decodeX(int x) {
4142
public double decodeY(int y) {
4243
return GeoEncodingUtils.decodeLatitude(y);
4344
}
45+
46+
@Override
47+
public double normalizeX(double x) {
48+
return GeoUtils.normalizeLon(x);
49+
}
50+
51+
@Override
52+
public double normalizeY(double y) {
53+
return GeoUtils.normalizeLat(y);
54+
}
4455
}

x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/GeoShapeValues.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ public static GeoShapeValue missing(String missing) {
133133
try {
134134
final GeoShapeIndexer indexer = new GeoShapeIndexer(true, "missing");
135135
final Geometry geometry = indexer.prepareForIndexing(MISSING_GEOMETRY_PARSER.fromWKT(missing));
136-
final BinaryGeoShapeDocValuesField field =
137-
new BinaryGeoShapeDocValuesField(missing, indexer.indexShape(null, geometry), new CentroidCalculator(geometry));
136+
final BinaryGeoShapeDocValuesField field = new BinaryGeoShapeDocValuesField("missing");
137+
field.add(indexer.indexShape(null, geometry), geometry);
138138
final GeometryDocValueReader reader = new GeometryDocValueReader();
139139
reader.reset(field.binaryValue());
140140
return new GeoShapeValue(reader);

x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/GeometryDocValueWriter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ public static BytesRef write(List<IndexableField> fields,
2727
CoordinateEncoder coordinateEncoder,
2828
CentroidCalculator centroidCalculator) throws IOException {
2929
final ByteBuffersDataOutput out = new ByteBuffersDataOutput();
30-
out.writeInt(coordinateEncoder.encodeX(centroidCalculator.getX()));
31-
out.writeInt(coordinateEncoder.encodeY(centroidCalculator.getY()));
30+
// normalization may be required due to floating point precision errors
31+
out.writeInt(coordinateEncoder.encodeX(coordinateEncoder.normalizeX(centroidCalculator.getX())));
32+
out.writeInt(coordinateEncoder.encodeY(coordinateEncoder.normalizeY(centroidCalculator.getY())));
3233
centroidCalculator.getDimensionalShapeType().writeTo(out);
3334
out.writeVLong(Double.doubleToLongBits(centroidCalculator.sumWeight()));
3435
TriangleTreeWriter.writeTo(out, fields);

x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/BinaryGeoShapeDocValuesField.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.apache.lucene.index.IndexableField;
1010
import org.apache.lucene.util.BytesRef;
1111
import org.elasticsearch.ElasticsearchException;
12+
import org.elasticsearch.geometry.Geometry;
1213
import org.elasticsearch.index.mapper.CustomDocValuesField;
1314
import org.elasticsearch.xpack.spatial.index.fielddata.CentroidCalculator;
1415
import org.elasticsearch.xpack.spatial.index.fielddata.CoordinateEncoder;
@@ -23,16 +24,15 @@ public class BinaryGeoShapeDocValuesField extends CustomDocValuesField {
2324
private final List<IndexableField> fields;
2425
private final CentroidCalculator centroidCalculator;
2526

26-
public BinaryGeoShapeDocValuesField(String name, List<IndexableField> fields, CentroidCalculator centroidCalculator) {
27+
public BinaryGeoShapeDocValuesField(String name) {
2728
super(name);
28-
this.fields = new ArrayList<>(fields.size());
29-
this.centroidCalculator = centroidCalculator;
30-
this.fields.addAll(fields);
29+
this.fields = new ArrayList<>();
30+
this.centroidCalculator = new CentroidCalculator();
3131
}
3232

33-
public void add( List<IndexableField> fields, CentroidCalculator centroidCalculator) {
33+
public void add(List<IndexableField> fields, Geometry geometry) {
3434
this.fields.addAll(fields);
35-
this.centroidCalculator.addFrom(centroidCalculator);
35+
this.centroidCalculator.add(geometry);
3636
}
3737

3838
@Override

x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/mapper/GeoShapeWithDocValuesFieldMapper.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import org.elasticsearch.index.query.VectorGeoShapeQueryProcessor;
3131
import org.elasticsearch.search.lookup.SearchLookup;
3232
import org.elasticsearch.xpack.spatial.index.fielddata.AbstractLatLonShapeIndexFieldData;
33-
import org.elasticsearch.xpack.spatial.index.fielddata.CentroidCalculator;
3433
import org.elasticsearch.xpack.spatial.search.aggregations.support.GeoShapeValuesSourceType;
3534

3635
import java.util.Arrays;
@@ -119,16 +118,12 @@ public GeoShapeWithDocValuesFieldMapper build(ContentPath contentPath) {
119118
@Override
120119
@SuppressWarnings({"rawtypes", "unchecked"})
121120
protected void addDocValuesFields(String name, Geometry shape, List<IndexableField> fields, ParseContext context) {
122-
CentroidCalculator calculator = new CentroidCalculator(shape);
123-
BinaryGeoShapeDocValuesField docValuesField =
124-
(BinaryGeoShapeDocValuesField) context.doc().getByKey(name);
121+
BinaryGeoShapeDocValuesField docValuesField = (BinaryGeoShapeDocValuesField) context.doc().getByKey(name);
125122
if (docValuesField == null) {
126-
docValuesField = new BinaryGeoShapeDocValuesField(name, fields, calculator);
123+
docValuesField = new BinaryGeoShapeDocValuesField(name);
127124
context.doc().addWithKey(name, docValuesField);
128-
129-
} else {
130-
docValuesField.add(fields, calculator);
131125
}
126+
docValuesField.add(fields, shape);
132127
}
133128

134129
public static final class GeoShapeWithDocValuesFieldType extends AbstractShapeGeometryFieldType implements GeoShapeQueryable {

x-pack/plugin/spatial/src/test/java/org/elasticsearch/xpack/spatial/aggregations/metrics/GeoShapeCentroidAggregatorTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ public void testSingleValuedField() throws Exception {
146146
// do not include geometry
147147
}
148148
// find dimensional-shape-type of geometry
149-
CentroidCalculator centroidCalculator = new CentroidCalculator(geometry);
149+
CentroidCalculator centroidCalculator = new CentroidCalculator();
150+
centroidCalculator.add(geometry);
150151
DimensionalShapeType geometryShapeType = centroidCalculator.getDimensionalShapeType();
151152
targetShapeType = targetShapeType.compareTo(geometryShapeType) >= 0 ? targetShapeType : geometryShapeType;
152153
}
@@ -157,7 +158,8 @@ public void testSingleValuedField() throws Exception {
157158
CompensatedSum compensatedSumWeight = new CompensatedSum(0, 0);
158159
for (Geometry geometry : geometries) {
159160
Document document = new Document();
160-
CentroidCalculator calculator = new CentroidCalculator(geometry);
161+
CentroidCalculator calculator = new CentroidCalculator();
162+
calculator.add(geometry);
161163
document.add(GeoTestUtils.binaryGeoShapeDocValuesField("field", geometry));
162164
w.addDocument(document);
163165
if (targetShapeType.compareTo(calculator.getDimensionalShapeType()) == 0) {

0 commit comments

Comments
 (0)