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

Add winding order for linestrings #132

Closed
wants to merge 1 commit into from

Conversation

amandasaurus
Copy link
Member

For some data formats (e.g. ESRI Shapefiles), rings in a polygon are stored in a specific winding order (clockwise for exterior ring, anti-clockwise for interior rings IIRC). So I added support to detect this, to make a linestring clockwise/counterclockwise, and to iterate over the points in that order.

The code is already there for the Area calculation. I split the shoelace formula code into it's own file, since it felt weird importing something from the area module into types. But I'm open to suggestions on this, because I'm not 100% sure it's the best approach.

} else if shoelace > T::zero() {
WindingOrder::CounterClockwise
} else if shoelace == T::zero() {
// what should be done here?
Copy link
Member

@mbattifarano mbattifarano Aug 5, 2017

Choose a reason for hiding this comment

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

It looks like shoelace == 0 when the line is degenerate (empty, single point, multiple coincident points) which brings us back to the validity conversation ( #123 #127 ).

It will also be zero for linestrings co-incident with the line y = x or either of the axes (and perhaps some other cases?).

I think changing the return type to Option<WindingOrder> would be a reasonable choice here.

Copy link
Member Author

Choose a reason for hiding this comment

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

I was considering making WindingOrder have 3 enum values: Clockwise, CounterClockwise" and Open` (as in 'this linestring is open, so there is no winding order'), since in theory this method can be called on every linestring, including linestrings which are not a closed ring.

However I didn't know about the other failure cases. So maybe a None would be better then.

/// Returns the winding order of this line
pub fn winding_order(&self) -> WindingOrder {
let shoelace = twice_area(self);
if shoelace < T::zero() {
Copy link
Member

Choose a reason for hiding this comment

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

imo I think a match here instead would be a little easier to read.

}
let mut tmp = T::zero();
for ps in linestring.0.windows(2) {
tmp = tmp + (ps[0].x() * ps[1].y() - ps[1].x() * ps[0].y());
Copy link
Member

@mbattifarano mbattifarano Aug 5, 2017

Choose a reason for hiding this comment

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

maybe for another PR, but does it make sense to extract the cross product into a trait?

Copy link
Member

Choose a reason for hiding this comment

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

@mbattifarano we're also already computing it in convexhull.rs although in a slightly different way, and it's used in a couple of places in the Polygon Distance PR, so it's probably worth thinking about how to unify them.

Copy link
Member

Choose a reason for hiding this comment

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

@urschrei I agree. It also crops up in #122. I've opened up an issue to that effect #133

assert_eq!(new_line2.winding_order(), WindingOrder::CounterClockwise);
assert_ne!(new_line2, cw_line);
assert_eq!(new_line2, ccw_line);

Copy link
Member

Choose a reason for hiding this comment

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

I think it would be worth testing a few cases where we should expect a panic.

@urschrei
Copy link
Member

urschrei commented Aug 5, 2017

This seems relevant to the Orient trait, although I'm not sure how best to combine them…

@amandasaurus
Copy link
Member Author

This seems relevant to the Orient trait, although I'm not sure how best to combine them…

🤦 Gah! I didn't know about that! I saw the file and just thought it was something to do with rotations. That'll teach me for redoing work.

Yes, there shouldn't be both, and hence it makes sense to "merge" them somehow.

@urschrei
Copy link
Member

urschrei commented Aug 6, 2017

I'm not tied to Orient (I stole the name from Shapely's method of the same name), its functionality could easily be re-implemented on top of this PR, and if we iterate on this one, we only have to worry about the edge cases @mbattifarano pointed out in #132 (comment) – which I also failed to consider – in one place.

@amandasaurus
Copy link
Member Author

I've redid this as #134, so I'm closing this.

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.

3 participants