-
Notifications
You must be signed in to change notification settings - Fork 449
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
Conversation
@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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
1c470e5
to
44dbbfb
Compare
There was a problem hiding this 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.
@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]>
44dbbfb
to
36b8ae3
Compare
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()); |
There was a problem hiding this comment.
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!)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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!
@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]>
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; |
There was a problem hiding this comment.
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.
@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) |
There was a problem hiding this comment.
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.
@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/