Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve MultiPolygon centroid robustness #118

Merged
merged 4 commits into from
Aug 22, 2017

Conversation

dbaston
Copy link
Contributor

@dbaston dbaston commented Aug 19, 2017

@jnh5y as we discussed the other day: this PR improves the robustness of centroid computations for MultiPolygons with disparate components. It is a very late resolution to the topic discussed in this thread: https://sourceforge.net/p/jts-topo-suite/mailman/message/31595183/

@jnh5y
Copy link
Contributor

jnh5y commented Aug 19, 2017

@dbaston great! There are a few things to iron out around contributing to an Eclipse project. From the https://github.com/locationtech/jts/blob/master/CONTRIBUTING.md, you'll need to sign the ECA (https://www.eclipse.org/legal/ECA.php) and 'sign' each git commit with 'git commit -s'.

@@ -153,7 +153,6 @@ else if (ptCount > 0){

private void setBasePoint(Coordinate basePt)
{
if (this.areaBasePt == null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting This fixes the issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the test case I included, yes.

@dbaston dbaston force-pushed the centroid-robustness branch from 1c470e5 to 44dbbfb Compare August 19, 2017 15:27
Copy link
Contributor

@jnh5y jnh5y left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dan and I chatted about this in person at the FOSS4G Boston code sprint.

The key issue is that multipolygons should reset the basePoint for each component.

@jnh5y
Copy link
Contributor

jnh5y commented Aug 19, 2017

@dr-jts, any input on this? Looks good to me. Dan explained it in person and it sounds reasonable.

Do this by allowing each polygon shell to set its own origin point,
instead of re-using the origin point from the first polygon in
the geometry.

Signed-off-by: Daniel Baston <[email protected]>
@dbaston dbaston force-pushed the centroid-robustness branch from 44dbbfb to 36b8ae3 Compare August 19, 2017 16:50
@dr-jts
Copy link
Contributor

dr-jts commented Aug 21, 2017

The fix looks good. I am going to make some comments on the PR.

@dbaston are you going to fill out an ECA? Seems worthwhile, since you've been so productive with JTS enhancements.

cy += areaFraction * componentCentroid.y;
}

assertEquals(new Coordinate(cx, cy), g.getCentroid().getCoordinate());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems highly susceptible to round-off issues. Does it really work in this case?

Would be better to use a precision tolerance in the comparison, I think.

Also would be good to factor the area-weighted algorithm out into a separate method, both to highlight that it is an alternative algorithm to the current polygon centroid computation, and to allow reusing it for other test cases. (Given this nice addition of a Centroid unit test, it would be nice to add more complete coverage to the tests it contains. Also, that would provide better coverage of the area-weighted algorithm - which of course also needs to be "proven out" by some testing!)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can factor the weighted-average centroid out for use in other tests, but the intent wasn't to propose it as a separate algorithm for general use. The intent with this test was only to show that the change to the base algorithm (moving the triangle base point for each polygon shell) produces results that are equivalent to the area-weighted average described in the linked email chain.

The results satisfy Coordinate::equals in this case but may suffer from round-off discrepancies with other inputs. I can change the assertion to check that the distance between the two centroids is < 1e-14 or something, if that is preferable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just mean factoring it out as a method in the CentroidTest class. That shows that it is useful for building tests, but doesn't promote using it for actual computation.

Adding a tolerance would be great. Note that Coordinate provides an equals2D-with-tolerance method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. I've made these changes.

-92.65512450000065 36.586800000000466
)))
</a>
<test><op name="getCentroid" arg1="A" >POINT (-92.6553838608954 36.58695407733924)</op></test>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A similar comment here. This seems brittle WRT numeric precision. Not your problem, but it would be nice if the XML test syntax allowed defining a precision tolerance for expected results. (For centroid computation the tolerance can be quite fine, since the algorithm should produce very close results).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the XML Test format doesn't provide a way of expressing a tolerance value, this is fine as it stands. If it breaks in the future, that will be motivation to add a tolerance!

@dr-jts
Copy link
Contributor

dr-jts commented Aug 21, 2017

@dbaston would the same robustness issue occur if the polygons in the example were holes in a larger polygon? In this case I suspect the area base point should be reset for each ring?

Signed-off-by: Daniel Baston <[email protected]>
@dbaston
Copy link
Contributor Author

dbaston commented Aug 22, 2017

Yes, it intuitively seems beneficial to reset the base point for each ring. I don't have a test case showing failure without doing this, but I can make the change anyway if you like.

Oh, and yes, I filled out an ECA during the sprint on Saturday.


public class CentroidTest extends TestCase {

private static final double tolerance = 1e-10;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A minor style point - I like to make static final fieldnames uppercase, so it's obvious that they are constants.

@dr-jts
Copy link
Contributor

dr-jts commented Aug 22, 2017

@dbaston re setting base point for holes: on further reflection I suspect that the computation would be dominated by the larger enclosing shell, so that there's probably no need to worry about the affect of small holes. Without an example demonstrating the issue there's no need to make a fix for this.

@@ -153,7 +153,6 @@ else if (ptCount > 0){

private void setBasePoint(Coordinate basePt)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we rename this to setAreaBasePoint? Makes it a teeny bit more descriptive, and indicates that this functionality is only necessary for areas.

@dr-jts dr-jts merged commit b430ca9 into locationtech:master Aug 22, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants