22

Say I have the following Polygon and Point:

>>> poly = Polygon([(0, 0), (2, 8), (14, 10), (6, 1)])
>>> point = Point(12, 4)

enter image description here

I can calculate the point's distance to the polygon...

>>> dist = point.distance(poly)
>>> print(dist)
2.49136439561

...but I would like to know the coordinate of the point on the polygon border where that shortest distance measures to.

My initial approach is to buffer the point by its distance to the polygon, and find the point at which that circle is tangent to the polygon:

>>> buff = point.buffer(dist) 

enter image description here However, I'm not sure how to calculate that point. The two polygon's don't intersect so list(poly.intersection(buff)) will not give me that point.

Am I on the right track with this? Is there a more straightforward method?

Georgy
  • 6,348
  • 7
  • 46
  • 58
AJG519
  • 2,609
  • 5
  • 28
  • 53
  • 2
    Duplicate? http://stackoverflow.com/questions/10983872/distance-from-a-point-to-a-polygon – Oleg Sklyar Oct 23 '15 at 21:27
  • 1
    @Oleg, I don't believe this is a duplicate. As I mention above, I have no issue calculating the minimum distance to the polygon. I'm trying to find the point on the polygon boundary where that minimum distance is measured to. – AJG519 Oct 23 '15 at 21:42
  • Possible duplicate of [Coordinates of the closest points of two geometries in Shapely](https://stackoverflow.com/questions/24415806/coordinates-of-the-closest-points-of-two-geometries-in-shapely) – Georgy Jun 03 '19 at 09:06

3 Answers3

42

While the answer of eguaio does the job, there is a more natural way to get the closest point using shapely.ops.nearest_points function:

from shapely.geometry import Point, Polygon
from shapely.ops import nearest_points

poly = Polygon([(0, 0), (2, 8), (14, 10), (6, 1)])
point = Point(12, 4)
# The points are returned in the same order as the input geometries:
p1, p2 = nearest_points(poly, point)
print(p1.wkt)
# POINT (10.13793103448276 5.655172413793103)

The result is the same as in the other answer:

from shapely.geometry import LinearRing
pol_ext = LinearRing(poly.exterior.coords)
d = pol_ext.project(point)
p = pol_ext.interpolate(d)
print(p.wkt)
# POINT (10.13793103448276 5.655172413793103)
print(p.equals(p1))
# True
Georgy
  • 6,348
  • 7
  • 46
  • 58
  • 2
    This should be the correct answer now. This function was not available at the time I wrote the original answer. It seems I should re-read the library documentation now. – eguaio May 31 '19 at 17:36
  • @eguaio [Version history](https://shapely.readthedocs.io/en/latest/project.html#id29) and [GitHub](https://github.com/Toblerity/Shapely/pull/147) say that `nearest_points` was added in 2014, though. – Georgy May 31 '19 at 18:44
  • 1
    I should read the documentation more carefully then! @Georgy thank you for the contribution. – eguaio May 31 '19 at 22:21
  • 2
    If your point is inside the polygon you can use: `p1, p2 = nearest_points(poly.boundary, point)` – Nils May 02 '20 at 13:57
  • @Georgy - Need your help to get the distance between the farthest point of a polygon to a point outside the polygon. (lat lon given) – Zeeshan Eqbal Apr 21 '21 at 07:09
  • 1
    @ZeeshanEqbal This should answer your question: [Find longest “straight” path between Point and Polygon](https://stackoverflow.com/q/46259590/7851470) – Georgy Apr 21 '21 at 08:17
  • @Georgy Thanks for your quick response. But will it work with latitude and long? – Zeeshan Eqbal Apr 21 '21 at 09:50
  • @ZeeshanEqbal Depends on what units you expect the result to have, I suppose. The answer to the question I linked says that you will have to first convert the coordinates. Unfortunately, I'm not the best person to help with that. :) See the links under that answer. Maybe there is something useful there. – Georgy Apr 21 '21 at 10:30
32

Please, do not up-vote this answer, the correct answer is @Georgy 's answer below.

My answer for reference:

There is an easy way to do this relying on Shapely functions. First, you need to get the exterior ring of the polygon and project the point to the ring. It is mandatory to get the exterior as a LinearRing since polygons do not have the projection function. Opposed to intuition, this gives a distance, the distance from the first point of the ring to the point in the ring closest to the given point. Then, you just use that distance to get the point with the interpolate function. See the code below.

from shapely.geometry import Polygon, Point, LinearRing

poly = Polygon([(0, 0), (2,8), (14, 10), (6, 1)])
point = Point(12, 4)

pol_ext = LinearRing(poly.exterior.coords)
d = pol_ext.project(point)
p = pol_ext.interpolate(d)
closest_point_coords = list(p.coords)[0]

It is important to mention that this method only works if you know the point is outside the exterior of the polygon. If the point is inside one of its interior rings, you need to adapt the code for that situation.

If the polygon doesn't have interior rings, the code will work even for points inside the polygon. That is because we are in fact working with the exterior ring as a line string, and ignoring whether the line string comes from a polygon or not.

It is easy to extend this code to the general case of computing the distance of any point (inside or outside of the polygon) to the closest point in the polygon boundary. You only need to compute the closest point (and distance) from the point to all line rings: the exterior ring, and each interior ring of the polygon. Then, you just keep the minimum of those.

enter image description here

eguaio
  • 3,124
  • 1
  • 18
  • 35
  • this is perfect. I was not familiar with those Shapely functions. Can this also work if the exterior point was instead a polygon? I tried using pol_ext.project() to both a polygons and to a linestring, but get the error message "third argument of GEOSProject_r must be Point*". Any suggestions on this? – AJG519 Oct 26 '15 at 18:30
  • Not that I know. I think for that you have no choice other than implementing something yourself. Tome Karzes answer can be easily addapted to that scenario (but using one of the polygons as "the point", since you have the above method). – eguaio Oct 27 '15 at 02:16
  • In the answer of the question http://stackoverflow.com/questions/38514607/find-shortest-path-from-one-geometry-to-other-on-shapely/38997756#38997756 you can find an implementation of Tom Karzes suggested algorithm to get the distance between two not crossing linestrings. The code there can be easily adapted to get the distance between a linestring and a point, and does not depend on shapely functions. – eguaio Oct 12 '16 at 12:11
  • Thanks, this is helpful. Can you perhaps go into more detail as to why it doesn't work when the point is inside the polygon? I ran this code a couple times with different points, and the result _looked_ like it was correct. – xandermonkey Apr 03 '17 at 00:03
  • Sure. I'll complete the answer. I assume your polygons do not have interior rings, in that case, the code will indeed work. – eguaio Apr 03 '17 at 11:39
0

There are two cases two consider: (1) the closest point lies on an edge and (2) the closest point is a vertex. Case (2) is easy to check - just take the distance to each vertex and find the minimum. Case (1) involves a little more math but still isn't too bad. You need to do two things for case (1): (a) find where the normal from the point to the edge intersects the edge, and (b) verify that it lies within the line segment (as opposed to extending past one of the ends). If it's not on the line segment, ignore it (one of the vertices will be the closest point on that edge).

Tom Karzes
  • 17,131
  • 2
  • 14
  • 32