Skip to content

Commit

Permalink
update centroid calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
talevy committed Dec 6, 2019
1 parent 0fda5b6 commit a713f5c
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@

package org.elasticsearch.common.geo;

import org.elasticsearch.geometry.Line;
import org.elasticsearch.geometry.LinearRing;
import org.elasticsearch.geometry.Point;
import org.elasticsearch.geometry.Polygon;
import org.elasticsearch.geometry.Rectangle;

/**
* This class keeps a running Kahan-sum of coordinates
* that are to be averaged in {@link TriangleTreeWriter} for use
Expand All @@ -30,14 +36,66 @@ public class CentroidCalculator {
private double compY;
private double sumX;
private double sumY;
private double sumWeight;
private int count;

// TODO(keep track of highest dimension)
public CentroidCalculator() {
this.sumX = 0.0;
this.compX = 0.0;
this.sumY = 0.0;
this.compY = 0.0;
this.count = 0;
this.sumWeight = 0;
}

/**
* @return the x-coordinate centroid
*/
public double getX() {
return sumX / sumWeight;
}

/**
* @return the y-coordinate centroid
*/
public double getY() {
return sumY / sumWeight;
}

public void addLine(Line line) {
for (int i = 0; i < line.length() - 1; i++) {
double diffX = line.getX(i) - line.getX(i + 1);
double diffY = line.getY(i) - line.getY(i + 1);
double weight = Math.sqrt(diffX * diffX + diffY * diffY);
double x = weight * (line.getX(i) + line.getX(i + 1)) / 2;
double y = weight * (line.getY(i) + line.getY(i + 1)) / 2;
addWeightedCoordinate(x, y, weight);
}
}

public void addPolygon(Polygon polygon) {
addLinearRing(polygon.getPolygon(), 1);
for (int i = 0; i < polygon.getNumberOfHoles(); i++) {
addLinearRing(polygon.getHole(i), -1);
}
}

private void addLinearRing(LinearRing ring, int sign) {
for (int i = 0; i < ring.length() - 1; i++) {
double weight = ring.getX(i) * ring.getY(i + 1) + ring.getY(i) * ring.getX(i + 1);
addWeightedCoordinate((ring.getX(i) + ring.getX(i + 1)) / 2, (ring.getY(i) + ring.getY(i + 1)) / 2, sign * weight);
}
}

public void addRectangle(Rectangle rectangle) {
double diffX = rectangle.getMaxX() - rectangle.getMinX();
double diffY = rectangle.getMaxY() - rectangle.getMinY();
addWeightedCoordinate(diffX / 2, diffY / 2, diffX * diffY);
}

public void addPoint(Point point) {
addWeightedCoordinate(point.getX(), point.getY(), 1);
}

/**
Expand All @@ -46,8 +104,9 @@ public CentroidCalculator() {
*
* @param x the x-coordinate of the point
* @param y the y-coordinate of the point
* @param weight the weight of the point
*/
public void addCoordinate(double x, double y) {
public void addWeightedCoordinate(double x, double y, double weight) {
double correctedX = x - compX;
double newSumX = sumX + correctedX;
compX = (newSumX - sumX) - correctedX;
Expand All @@ -59,6 +118,7 @@ public void addCoordinate(double x, double y) {
sumY = newSumY;

count += 1;
sumWeight += weight;
}

/**
Expand All @@ -70,22 +130,8 @@ public void addCoordinate(double x, double y) {
* @param otherCalculator the other centroid calculator to add from
*/
void addFrom(CentroidCalculator otherCalculator) {
addCoordinate(otherCalculator.sumX, otherCalculator.sumY);
addWeightedCoordinate(otherCalculator.sumX, otherCalculator.sumY, otherCalculator.sumWeight);
// adjust count
count += otherCalculator.count - 1;
}

/**
* @return the x-coordinate centroid
*/
public double getX() {
return sumX / count;
}

/**
* @return the y-coordinate centroid
*/
public double getY() {
return sumY / count;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,7 @@ public Void visit(GeometryCollection<?> collection) {

@Override
public Void visit(Line line) {
for (int i = 0; i < line.length(); i++) {
centroidCalculator.addCoordinate(line.getX(i), line.getY(i));
}
centroidCalculator.addLine(line);
org.apache.lucene.geo.Line luceneLine = GeoShapeIndexer.toLuceneLine(line);
addToExtent(luceneLine.minLon, luceneLine.maxLon, luceneLine.minLat, luceneLine.maxLat);
addTriangles(TriangleTreeLeaf.fromLine(coordinateEncoder, luceneLine));
Expand All @@ -142,10 +140,7 @@ public Void visit(MultiLine multiLine) {

@Override
public Void visit(Polygon polygon) {
// TODO: Shall we consider holes for centroid computation?
for (int i =0; i < polygon.getPolygon().length() - 1; i++) {
centroidCalculator.addCoordinate(polygon.getPolygon().getX(i), polygon.getPolygon().getY(i));
}
centroidCalculator.addPolygon(polygon);
org.apache.lucene.geo.Polygon lucenePolygon = GeoShapeIndexer.toLucenePolygon(polygon);
addToExtent(lucenePolygon.minLon, lucenePolygon.maxLon, lucenePolygon.minLat, lucenePolygon.maxLat);
addTriangles(TriangleTreeLeaf.fromPolygon(coordinateEncoder, lucenePolygon));
Expand All @@ -162,16 +157,15 @@ public Void visit(MultiPolygon multiPolygon) {

@Override
public Void visit(Rectangle r) {
centroidCalculator.addCoordinate(r.getMinX(), r.getMinY());
centroidCalculator.addCoordinate(r.getMaxX(), r.getMaxY());
centroidCalculator.addRectangle(r);
addToExtent(r.getMinLon(), r.getMaxLon(), r.getMinLat(), r.getMaxLat());
addTriangles(TriangleTreeLeaf.fromRectangle(coordinateEncoder, r));
return null;
}

@Override
public Void visit(Point point) {
centroidCalculator.addCoordinate(point.getX(), point.getY());
centroidCalculator.addPoint(point);
addToExtent(point.getLon(), point.getLon(), point.getLat(), point.getLat());
addTriangles(TriangleTreeLeaf.fromPoints(coordinateEncoder, point));
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,65 @@
*/
package org.elasticsearch.common.geo;

import org.elasticsearch.geometry.Line;
import org.elasticsearch.geometry.LinearRing;
import org.elasticsearch.geometry.Point;
import org.elasticsearch.geometry.Polygon;
import org.elasticsearch.test.ESTestCase;

import java.util.Collections;

import static org.hamcrest.Matchers.equalTo;

public class CentroidCalculatorTests extends ESTestCase {

public void test() {
public void testPoints() {
double[] x = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
double[] y = new double[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
double[] xRunningAvg = new double[] { 1, 1.5, 2.0, 2.5, 3, 3.5, 4, 4.5, 5, 5.5 };
double[] yRunningAvg = new double[] { 10, 15, 20, 25, 30, 35, 40, 45, 50, 55 };
CentroidCalculator calculator = new CentroidCalculator();
for (int i = 0; i < 10; i++) {
calculator.addCoordinate(x[i], y[i]);
calculator.addPoint(new Point(x[i], y[i]));
assertThat(calculator.getX(), equalTo(xRunningAvg[i]));
assertThat(calculator.getY(), equalTo(yRunningAvg[i]));
}
CentroidCalculator otherCalculator = new CentroidCalculator();
otherCalculator.addCoordinate(0.0, 0.0);
otherCalculator.addPoint(new Point(0.0, 0.0));
calculator.addFrom(otherCalculator);
assertThat(calculator.getX(), equalTo(5.0));
assertThat(calculator.getY(), equalTo(50.0));
}

public void testLines() {
double[] x = new double[] { 1, 2, 3, 5 };
double[] y = new double[] { 10, 20, 30, 50 };
Line line = new Line(x, y);
CentroidCalculator calculator = new CentroidCalculator();
calculator.addLine(line);
assertThat(calculator.getX(), equalTo(3.0));
assertThat(calculator.getY(), equalTo(30.0));
}

public void testPolygon() {
double[] px = {0, 10, 10, 20, 20, 30, 30, 40, 40, 50, 50, 0, 0};
double[] py = {0, 0, 20, 20, 0, 0, 20, 20, 0, 0, 30, 30, 0};

double[] hx = {21, 21, 29, 29, 21};
double[] hy = {1, 20, 20, 1, 1};

Polygon polyWithHole = new Polygon(new LinearRing(px, py), Collections.singletonList(new LinearRing(hx, hy)));
CentroidCalculator calculator = new CentroidCalculator();
calculator.addPolygon(polyWithHole);
assertThat(calculator.getX(), equalTo(3.0));
assertThat(calculator.getY(), equalTo(30.0));
}

public void testPolygonAndMore() {
// TODO
}

public void testLinesAndPoints() {
// TODO
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ public void testGeoShapeField() throws Exception {
geometry.visit(new GeometryVisitor<Void, Exception>() {
@Override
public Void visit(Circle circle) throws Exception {
calculator.addCoordinate(circle.getX(), circle.getY());
return null;
}

Expand All @@ -190,17 +189,12 @@ public Void visit(GeometryCollection<?> collection) throws Exception {

@Override
public Void visit(Line line) throws Exception {
for (int i = 0; i < line.length(); i++) {
calculator.addCoordinate(line.getX(i), line.getY(i));
}
calculator.addLine(line);
return null;
}

@Override
public Void visit(LinearRing ring) throws Exception {
for (int i = 0; i < ring.length() - 1; i++) {
calculator.addCoordinate(ring.getX(i), ring.getY(i));
}
return null;
}

Expand Down Expand Up @@ -230,7 +224,7 @@ public Void visit(MultiPolygon multiPolygon) throws Exception {

@Override
public Void visit(Point point) throws Exception {
calculator.addCoordinate(point.getX(), point.getY());
calculator.addPoint(point);
return null;
}

Expand All @@ -241,16 +235,13 @@ public Void visit(Polygon polygon) throws Exception {

@Override
public Void visit(Rectangle rectangle) throws Exception {
calculator.addCoordinate(rectangle.getMinX(), rectangle.getMinY());
calculator.addCoordinate(rectangle.getMinX(), rectangle.getMaxY());
calculator.addCoordinate(rectangle.getMaxX(), rectangle.getMinY());
calculator.addCoordinate(rectangle.getMaxX(), rectangle.getMaxY());
calculator.addRectangle(rectangle);
return null;
}
});
document.add(new BinaryGeoShapeDocValuesField("field", geometry));
w.addDocument(document);
centroidOfCentroidsCalculator.addCoordinate(calculator.getX(), calculator.getY());
centroidOfCentroidsCalculator.addWeightedCoordinate(calculator.getX(), calculator.getY(), 1);
}
expectedCentroid.reset(centroidOfCentroidsCalculator.getY(), centroidOfCentroidsCalculator.getX());
assertCentroid(w, expectedCentroid, new GeoShapeFieldMapper.GeoShapeFieldType());
Expand Down

0 comments on commit a713f5c

Please sign in to comment.