Find coordinate of the closest point on polygon in Shapely - python

Say I have the following Polygon and Point:
>>> poly = Polygon([(0, 0), (2, 8), (14, 10), (6, 1)])
>>> point = Point(12, 4)
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)
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?

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

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.

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).

I liked the idea of intersecting the polygon poly with a circle buff centered at the point point, just as you wrote in your question. I would suggest:
poly.boundary.intersection(buff.boundary)

Related

Checking if a Polygon contains MultiPoint in Shapely returns unexpected results

Can you explain the behaviour of Shapely contains method? Why is the first result below False and the second result True?
from shapely.geometry import Polygon, Point, MultiPoint
poly = Polygon([[0,0], [2, 0], [2, 2], [0, 2]])
poly.contains(MultiPoint([Point(2,2)]))
Out[3]: False
poly.contains(MultiPoint([Point(2,2), Point(1,1)]))
Out[4]: True
poly.contains(MultiPoint([Point(2,2), Point(1,1), Point(3,3)]))
Out[5]: False
Citing the docs on object.contains(other):
Returns True if no points of other lie in the exterior of the object and at least one point of the interior of other lies in the interior of object.
So, in fact, everything works as intended.
1) When you check poly.contains(MultiPoint([Point(2,2)])), the point doesn't lie in the interior of poly but on its boundary. Therefore it returns False.
2) When you check poly.contains(MultiPoint([Point(2,2), Point(1,1)])), no points of the MultiPoint object lie in the exterior of poly and there is one point that lies in its interior. This satisfies the given conditions.
3) For poly.contains(MultiPoint([Point(2,2), Point(1,1), Point(3,3)])) case there is one point that lies in the exterior of poly, hence the False as a result.
P.S.: You wrote in the comment:
Regarding 2) the point Point(2,2) lies in the exterior of poly, i.e. poly.exterior.contains(Point(2,2)) returns True.
This exterior attribute of a polygon actually means the exterior LinearRing of a polygon. And it is not equal to the exterior points set. Docs on Polygon say:
Component rings are accessed via exterior and interiors properties.
The interior, boundary, and exterior points sets are defined here as follows:
A Surface has an interior set consisting of the infinitely many points within (imagine a Curve dragged in space to cover an area), a boundary set consisting of one or more Curves, and an exterior set of all other points including those within holes that might exist in the surface.

Get a point on a plane at specific distance from initial point

I got this plane
from sympy.geometry.plane import Plane
p=Plane((0,5,0),(0,0,0),(5,7,0)) #2d first to make it easier
now I want to get a point on that plane 45deg from plane point one and lenght=sqrt(2). In this case that point will be (1,6,0)
I've tried this:
a=np.sqrt(2)*p.arbitrary_point(pi/4)
but it does not work as a coordinates return (1.0,8.07106781187,0.0)
the problem is that arbitrary_point returns a point in a circle of radius 1 about p1 of the Plane. I want to be able to change that radius.
Multiplying all of a point's coordinates by sqrt(2) moves it away from the origin (0,0,0). You want to move it away from p1 of the plane. This is what the scale method is for.
p.arbitrary_point(np.pi/4).scale(np.sqrt(2), np.sqrt(2), np.sqrt(2), p.p1)
returns Point3D(1, 6, 0).
(I'm assuming import numpy as np here, to match the setting of the question; the standard math module could be used instead of numpy.)

Finding coordinate points of intersection with two numpy arrays

This sort of question is a tad bit different the normal 'how to find the intersection of two lines' via numpy. Here is the situation, I am creating a program that looks at slope stability and I need to find where a circle intersects a line.
I have two numpy arrays:
One array gives me a normal (x, y) values of an elevation profile in 2D
The other array is calculated values of coordinates (x, y) that spans the circumference of a circle from a defined centre.
I need to somehow compare the two at what approximate point does the coordinates of the circle intersect the profile line?
Here some data to work with:
circ_coords = np.array([
[.71,.71],
[0.,1.]
])
linear_profile = np.array([
[0.,0.],
[1.,1.]
])
I need a function that would spit out say a single or multiple coordinate values saying that based on these circular coordinates and your linear profile.. the two would intersect here.
def intersect(array1, array2):
# stuff
return computed_array
You can solve it algebraically. The parametric representation of points (x,y) on the line segment between (x1,y1) and (x2,y2) is:
x=tx1+(1−t)x2 and y=ty1+(1−t)y2,
where 0≤t≤1.
If you substitute it in the equation of the circle and solve the resulting quadratic equation for t, you can test if 0≤t01≤1, i.e line segment intersets with circle. The t01 values could be than used to calculate intersection points.
Shapely has some cool functions. According to this post, this code should work:
from shapely.geometry import LineString
from shapely.geometry import Point
p = Point(0,0)//center
c = p.buffer(0.71).boundary//radius
l = LineString([(0.,0.), (1., 1.)])//line point
i = c.intersection(l)
Apparently here i is the array you are looking for, also, check this post too. Hope this helps.

Finding the distance of coordinates from the beginning of a route in Shapely

I have a list of coordinates (lat/lon) representing a route.
Given a certain radius and another coordinate I need to check if the coord is in the route (within the given radius from any point) and its distance from the beginning of the route.
I looked at Shapely and it looks like a good solution.
I started off by creating a StringLine
from shapely.geometry import LineString
route = LineString[(x, y), (x1, y1), ...]
Then to check if the point is near the route I've added a buffer and checked
for intersection
from shapely.geometry import Point
p = Point(x, y)
r = 0.5
intersection = p.buffer(r).intersection(route)
if intersection.is_empty:
print "Point not on route"
else:
# Calculate P distance from the begning of route
I'm stuck calculating the distance. I thought of splitting the route at p and measuring the length of the first half but the intersection result I get is a HeterogeneousGeometrySequence which I'm not sure what I can do with.
I believe I found a solution:
if p.buffer(r).intersects(route):
return route.project(p)
Rather than buffering a geometry, which is expensive and imperfect (since buffering requires a number of segments and many other options), just see if the point is within a distance threshold:
if route.distance(p) <= r:
return route.project(p)
Also, you probably realised by now that your distance units are in degrees. If you want linear distances, like meters, you would need to make it much more complicated using different libraries.

How to find delaunay triangulation facets containing the given points

I have plotted n random points (the black points) and used delaunay triangulation, now I want to interpolate m random evaluation points (the red points) so I need to calculate which triangle the evaluation point is inside.
What is the approach for calculating the vertices of the triangle for each point?
For a given triangle, ABC, a point is inside the triangle if it is on the same side of line AB as point C is, on the same side of line BC as point A is, and on the same side of line AC as point B is. You can pre-optimize this check for each triangle and check them all until you find the triangle(s) it is in. See this page for more details.
To save computation, you can compute the minimum and maximum X and Y coordinates of the points for each triangle. If the X and Y coordinates of a point are not within the minimum and maximum values, you can immediately skip checking that triangle. The point cannot be inside it if it isn't inside the rectangle that bounds the triangle.
I'll assume that triangles do not intersect except of common edges.
You don't want to check every triangle (or subset of them) independently. The main reason is computation errors - due to them you may get answer "inside" for more than one triangle (or zero) which may break logic of your program.
More robust way is:
Find closest edge to the point
Select one of triangles touching this edge
Make one check for that triangle (the point lies on the same side as the third triangle vertex)
If "inside" - return this triangle
If "outside" - return another triangle on this edge (or "nothing" if there is no other triangle)
Even if you will return wrong triangle because of computation error, there still be exactly one triangle and point will lie close enough to it to accept such mistakes.
For #1 you can use something like quad-tree as Michael Wild suggests.
This simple example triangulates 10 random points, a further 3 random points are generated and if they fall inside a triangle the vertices are given:
import numpy as np
from pyhull.delaunay import DelaunayTri
def sign(a,b,c):
return (a[0]-c[0])*(b[1]-c[1])-(b[0]-c[0])*(a[1]-c[1])
def findfacet(p,simplice):
c,b,a = simplice.coords
b1 = sign(p,a,b) < 0.0
b2 = sign(p,b,c) < 0.0
b3 = sign(p,c,a) < 0.0
return b1 == b2 == b3
data = np.random.randn(10, 2)
dtri = DelaunayTri(data)
interpolate = np.random.randn(3, 2)
for point in interpolate:
for triangle in dtri.simplices:
if findfacet(point,triangle):
print "Point",point,"inside",triangle.coords
break
Using matplotlib to visualize (code omitted):
The dotted cyan lines now connect the points to interpolate with the vertices of triangle it lays within. The black lines are the convex hull, and the solid cyan lines are the delaunay triangulation.
A Delaunay triangulation is in itself a search data structure. Your Delaunay triangulation implementation probably has location functions. How have you computed the Delaunay triangulation of your points?
CGAL has an implementation of 2D and 3D triangulations. The resulting data structure is able to localize any point using a walk from a given point. See for example that chapter of the manual. CGAL is a C++ library, but it has python bindings.

Categories

Resources