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

Fix polygon area calculation: use Shoelace formula #2927

Merged
merged 11 commits into from
Apr 7, 2021

Conversation

merkispavel
Copy link
Contributor

@merkispavel merkispavel commented Mar 9, 2021

https://en.wikipedia.org/wiki/Shoelace_formula

Issue

Isochrone api creates polygon away from the origin location. The reason is the broken polygon_area function. The area of the small polygon turnes out to be larger than the whole convex-hull-ish polygon. So if you set denoise = 1 you get the issue itself.

Screen Shot 2021-03-09 at 08 44 26

Screen Shot 2021-03-09 at 08 28 33

If you want to reproduce

request: '{"locations":[{"lat":51.2491202,"lon":0}],"costing":"pedestrian","contours":[{"time":20}], "show_locations":true, "polygons":true, "denoise":1}'

planet.tar.zip

Tasklist

  • Add tests
  • Add #fixes with the issue number that this PR addresses
  • Update the docs with any new request parameters or changes to behavior described
  • Update the changelog

Requirements / Relations

Link any requirements here. Other pull requests this PR is based on?

@merkispavel
Copy link
Contributor Author

I noticed that inner polygons have positive area sometimes. I think this might be the cause of the failed tests. Anyway, investigating..

@kevinkreiser
Copy link
Member

@merkispavel just an FYI recently @nilsnolde was also computing polygon area (using boost) in #2750 i wonder if both PRs could use the same method of computing polygon area. just wanted to flag it for you to have a look

@dnesbitt61
Copy link
Member

Polygon area (line 552 in midgard/util.cc) I believe should be:

area += (p1->first * p2->second) - (p1->second * p2->first)

This should work for any simple polygon.

@dnesbitt61
Copy link
Member

I verified that the change described above is correct. I wrote a simple test with a rectangle and the current polygon_area computation fails and the update is correct. Problem is this breaks the isochrone tests.

@dnesbitt61
Copy link
Member

I think the standard convention is for counter-clockwise orientations to produce positive area and clockwise orientations should yield negative area. A simple test (rectangle) could be added to test/util_midgard.cc:

TEST(UtilMidgard, Area) {
  // Clockwise polygon (rectangle). Should return negative area.
  using poly_t = std::vector<Point2>;
  poly_t poly{ {0, 0}, {0, 20}, {20, 20}, {20, 0}, {0, 0} };
  auto area = polygon_area(poly);
  EXPECT_EQ(area, -400.0);

  // Reverse the polygon (ccw) - should be positive area
  std::reverse(std::begin(poly), std::end(poly));
  area = polygon_area(poly);
  EXPECT_EQ(area, 400.0);
}

@merkispavel
Copy link
Contributor Author

Polygon area (line 552 in midgard/util.cc) I believe should be:

area += (p1->first * p2->second) - (p1->second * p2->first)

This should work for any simple polygon.

I verified that the change described above is correct. I wrote a simple test with a rectangle and the current polygon_area computation fails and the update is correct. Problem is this breaks the isochrone tests.

Right, these formulas are equivalent. I was too lazy to edit more than 2 symbols :) sorry

@merkispavel
Copy link
Contributor Author

I think the standard convention is for counter-clockwise orientations to produce positive area and clockwise orientations should yield negative area. A simple test (rectangle) could be added to test/util_midgard.cc:

TEST(UtilMidgard, Area) {
  // Clockwise polygon (rectangle). Should return negative area.
  using poly_t = std::vector<Point2>;
  poly_t poly{ {0, 0}, {0, 20}, {20, 20}, {20, 0}, {0, 0} };
  auto area = polygon_area(poly);
  EXPECT_EQ(area, -400.0);

  // Reverse the polygon (ccw) - should be positive area
  std::reverse(std::begin(poly), std::end(poly));
  area = polygon_area(poly);
  EXPECT_EQ(area, 400.0);
}

Totally agreed! I have more changes locally with unittests + comments, haven't pushed it yet
Moreover, some changes are needed not to lose polygon orientation in the isochrone code. This is done as well(haven't pushed it yet). I just need to figure out whether to push it to this branch or split to ease its review.
Anyway, I'll ping you when its ready(probably tomorrow)

@merkispavel
Copy link
Contributor Author

Another PR should be merged first in order to fix tests #2932

for (auto p1 = polygon.cbegin(), p2 = std::next(polygon.cbegin()); p2 != polygon.cend();
++p1, ++p2) {
area += (p1->first + p2->first) * (p1->second + p2->second);
area += p1->first * p2->second - p1->second * p2->first;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dnesbitt61 I changed the formula to its classic form not to confuse people in the future

Copy link
Member

Choose a reason for hiding this comment

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

thanks!

Copy link
Member

@dnesbitt61 dnesbitt61 left a comment

Choose a reason for hiding this comment

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

Looks good, thanks!

@kevinkreiser
Copy link
Member

@merkispavel if you could visually inspect the differences in the isochrone unit test and then update the values then we can get CI passing and we can merge it. looks good to me

* Track segment orientation

This is need to correctly join segments into rings

* Update changelog

* Remove ring reversing

* Fix: use multiline comment
@merkispavel
Copy link
Contributor Author

@kevinkreiser @dnesbitt61 the tests are fixed. Looks shippable in my view

CHANGELOG.md Outdated
Comment on lines 26 to 27
* FIXED: Skip bindings if there's no Python development version [#2893](https://github.com/valhalla/valhalla/pull/2893)
* FIXED: Skip bindings if there's no Python development version [#2893](https://github.com/valhalla/valhalla/pull/2878)
Copy link
Member

Choose a reason for hiding this comment

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

this seems wrong 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

damn :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The wrong link was used before so automerge decided to keep both entries. Fixed, thanks

@@ -98,7 +98,7 @@ template <std::size_t dimensions_t> class GriddedData : public Tiles<PointLL> {
typename PointLL::first_type s[5]; // Values at the tile corners and center
Copy link
Member

Choose a reason for hiding this comment

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

why is the code from #2932 in this PR as well? something seems messed up here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Originally i found 2 issues at once(polygon area bug and orientation bug) so I decided to make a hierarchy master <- 2927 <- 2932 for some reason 🤦(probably because of the unit tests) Now I think It brought more mess than clearness. Anyway, I had to fix unit tests so it didn't bring any profit
Would you like to split them @kevinkreiser ? (I guess I do, it'll just take a little more time and a few more approves)

Copy link
Member

Choose a reason for hiding this comment

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

oh no, no problem at all, i just missed a very important detail:

image

the previous one was merged into this branch not master. i completely missed that. this is fine!

std::array<std::function<void()>, 10> cases{
[]() {},
[&]() { from_pt = to_pt = {}; },
Copy link
Member

Choose a reason for hiding this comment

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

do you want to change that back to a no-op?

kevinkreiser
kevinkreiser previously approved these changes Apr 7, 2021
Copy link
Member

@kevinkreiser kevinkreiser left a comment

Choose a reason for hiding this comment

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

LGTM, previous review was the difficult part. fixing the area calculation was straight forward

kevinkreiser
kevinkreiser previously approved these changes Apr 7, 2021
@merkispavel merkispavel merged commit 2e74c32 into master Apr 7, 2021
@purew purew deleted the fix-polygon-area-formula branch April 7, 2021 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants