-
Notifications
You must be signed in to change notification settings - Fork 165
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 Segment Intersection Method and Sweepline Intersector Class #262
Conversation
…created SweeplineIntersector class to compute intersections on groups of segments. Segment#segment_intersection sligtly modifies the code from intersects_segment? and actually computes the intersection point (or nil if none). Edge cases for colinear segments are handled by return a single point from the intersection. intersects_segment? was modified to just check if segment_intersection is not nil. A SweeplineIntersector class was added that uses a basic sweepline algorithm to compute the intersections in a set of segments. This should be faster or as fast as the current validate_geometry intersection algorithm being used (more testing needed), since it only compares segments where their y-range includes the y value of the event the sweepline is handling. There are options to return all intersections or proper intersections that will filter out the connections in LineStrings. In the future, it is possible to make a Sweepline class and have the SweeplineIntersector inherit from it. This would allow us to use the sweepline more generically in other applications (some polygon clipping algos use a sweepline).
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.
Really cool, thank you for this implementation!
I feel like it could be in a separated file. But that is just IMHO.
I've made a few reviews, but everything feels great globally, it is just about tiny fixes.
On the giving segments versus points, I think even GEOS gives only points, hence I'm ensure we need to go that far!
lib/rgeo/cartesian/calculations.rb
Outdated
# a new line, it will check if it intersects with any of the segments | ||
# the line currently intersects at that y value. | ||
# This is a more simplistic implementation that uses an array to hold | ||
# observed segments instead of a sorted BST, so performance may be significantly |
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 am a bit unsure of where it would be more appropriate, could you indicate me ?
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.
LINESTRING(1 1, 0 1, 0 0, 0.125 0.5, 0.25 0, 0.375 0.5, 0.5 0, 0.625 0.5, 0.75 0, 0.875 0.5, 1 0, 1 1)
In this case, since we are comparing all observed_segments
against each other, this would be about O(n^2) since pretty much every segment is being observed at once here. If we were using a BST instead of an array or set, we would sort it so that only adjacent segments are compared to each other, but it's a lot more work to implement the tree and keep track of switches in order.
In this case,
LINESTRING (2.0 0.0, 1.847759065 -0.7653668647, 1.4142135624 -1.4142135624, 0.7653668647 -1.847759065, 0.0 -2.0, -0.7653668647 -1.847759065, -1.4142135624 -1.4142135624, -1.847759065 -0.7653668647, -2.0 0.0, -1.847759065 0.7653668647, -1.4142135624 1.4142135624, -0.7653668647 1.847759065, 0.0 2.0, 0.7653668647 1.847759065, 1.4142135624 1.4142135624, 1.847759065 0.7653668647, 2.0 0.0)
There are only ever 2 observed_segments
at once so it will run closer to O(nlogn).
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.
There's also another strategy that GEOS uses where they break the segments up into monotone pieces and run the intersection on each piece then combine the results.
I don't think it's really necessary we do either of these. If performance is that big of a concern you should use GEOS instead of the simpler Ruby implementation.
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.
@keithdoggett I definitely agree, thanks for the pointer though!
…rved_segments with Set, condense event creation branching.
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.
One last review, otherwise I think we're ready to ship, thanks!
This is the first of three cherry-picked commits from the
valid-op
branch.Summary
segment_intersection
method to determine the intersection point between two segments.SweeplineIntersector
class that can be used to determine the intersections between a list of segments.Other Information
I'm using a modified version of this algorithm for the
SweeplineIntersector
. Instead of using a BST for the status object, I'm just keeping all of the current segments in an array and comparing them all against each other. This means the worst case is O(n^2), but the average case is a lot better.Below is the results of a test for finding the intersections in a convex linear ring with various number of vertices vs. the current
is_simple?
algorithm.Current Algorithm:
SweeplineIntersector:
Future Work
Eventually, we may want to modify the
segment_intersection
method to return some type of struct that indicates the intersection type as well. Colinear intersections just return one point from the intersection, but in the future, we may want to get the subsegment from the intersection. This could cause issues with validity checking in a linear ring like this:LINESTRING(0 0, 1 0, 1 1, 0.5 1, 0.5 1.5, 0.5 1, 0 1, 0 0)
Where the segments in the spike might not be included in the
proper_intersections
array, but don't intersect in a valid way.