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

Implement Polygon-Polygon distance #219

Merged
merged 14 commits into from
May 14, 2018
Merged

Conversation

urschrei
Copy link
Member

@urschrei urschrei commented May 12, 2018

Uses the standard approach (brute force + R*-tree) for non-convex polygons, but for convex polygons, uses a novel implementation of the rotating calipers technique, based on Pirzadeh (1999), pp24—30, which is around 10x faster.

@urschrei
Copy link
Member Author

(Oh, it also adds LineString-LineString distance)

@urschrei urschrei requested a review from frewsxcv May 13, 2018 18:57
@urschrei urschrei force-pushed the new_polygon_distance branch from ac4d1cd to 928e4dc Compare May 13, 2018 18:59
// Return minimum distance between a Point and a Line segment
// This is a helper for Point-to-LineString and Point-to-Polygon distance
/// Minimum distance between a Point and a Line segment
/// This is a helper for Point-to-LineString and Point-to-Polygon distance
Copy link
Member

Choose a reason for hiding this comment

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

even though this won't get rendered might want to either update the next line to /// or revert the /// changes on the previous lines for consistency

match get_position(p, &poly.exterior) {
PositionPoint::Inside => true,
PositionPoint::OnBoundary | PositionPoint::Outside => false,
}
Copy link
Member

Choose a reason for hiding this comment

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

is this the same as doing:

poly.exterior.contains(p)

https://docs.rs/geo/0.8.3/geo/algorithm/contains/trait.Contains.html

Copy link
Member Author

Choose a reason for hiding this comment

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

contains() evaluates differently because of boundary detection IIRC…(it certainly starts failing the tests when I change it)

punit = unitvector(m, poly, p, idx);
} else {
match clockwise {
true => {
Copy link
Member

Choose a reason for hiding this comment

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

if ... {
} else {
    match clockwise {
        true => ...,
        false => ...,
    }
}

can be reduced to

if ... {
    ...
} else if clockwise {
    ...
} else {
    ...
}

vertical = true;
} else {
vertical = false;
}
Copy link
Member

Choose a reason for hiding this comment

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

let vertical = p.x() == u.x();

} else {
slope = (p.y() - u.y()) / (p.x() - u.x());
}
}
Copy link
Member

Choose a reason for hiding this comment

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

let slope = if vertical || p.y() == u.y() {
    T::zero()
}  else if u.x() > p.x() {
    (u.y() - p.y()) / (u.x() - p.x())
} else {
    (p.y() - u.y()) / (p.x() - u.x())
};

@urschrei
Copy link
Member Author

bors r=frewsxcv

bors bot added a commit that referenced this pull request May 14, 2018
219: Implement Polygon-Polygon distance r=frewsxcv a=urschrei

Uses the standard approach (brute force + R*-tree) for non-convex polygons, but for convex polygons, uses a novel implementation of the rotating calipers technique, based on [Pirzadeh (1999), pp24—30](http://digitool.library.mcgill.ca/R/?func=dbin-jump-full&object_id=21623&local_base=GEN01-MCG02), which is around 10x faster.

Co-authored-by: Stephan Hügel <urschrei@gmail.com>
@bors
Copy link
Contributor

bors bot commented May 14, 2018

Build succeeded

@bors bors bot merged commit f5188b1 into georust:master May 14, 2018
@urschrei
Copy link
Member Author

(did I say 10x? I just ran a benchmark, and the fast path is actually 100x faster)

fn euclidean_distance(&self, other: &Polygon<T>) -> T {
self.start
.euclidean_distance(other)
.min(self.end.euclidean_distance(other))
Copy link
Contributor

Choose a reason for hiding this comment

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

Q: does it mean that the distance from a Line to a Polygon is evaluated as either the distance from the starting point to the polygon, or the ending point to the polygon, depending on which one is closer?

What about this kind of situation for example:
screen shot 2018-05-15 at 16 44 03

Here, each endpoint of the line is much further to the polygon than the actual Line<>Polygon proper distance.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oof yes this is terrible.

urschrei added a commit to urschrei/geo that referenced this pull request May 15, 2018
@JivanRoquet
Copy link
Contributor

JivanRoquet commented May 15, 2018

As for Line <> MultiPolygon, do you think there could be further optimisations, or do you think a brute-force approach like below would be the most reasonable choice?

// Brute-force pseudocode
impl EuclideanDistance<T, MultiPolygon<T>> for Line<T> {
    fn euclidean_distance(&self, other: &MultiPolygon<T>) -> T {
        let mut distance = T::max_value();
        for polygon in other {
            let dist = self.euclidean_distance(polygon);
            distance = dist.min(distance);
        }
        distance
    }
}

@urschrei
Copy link
Member Author

I think that's a pretty reasonable implementation. I can't see an obviously simpler way to do it, and it's linear complexity (I think), so should be fine – it's always possible that there's a sub-linear approach, but I can't recall one off-hand.

@JivanRoquet
Copy link
Contributor

Just submitted a PR for that — only difference with above is I tried to follow the existing style.

See #227

urschrei added a commit to urschrei/geo that referenced this pull request May 16, 2018
bors bot added a commit that referenced this pull request May 16, 2018
226: Polygon-Line euclidean distance fix r=urschrei a=urschrei

See here: #219 (review)

WIP until I add tests for a Line contained in a Polygon interior ring and intersection 

Co-authored-by: Stephan Hügel <stephan.hugel.12@ucl.ac.uk>
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