Per @tcaswell's suggestion, shapely makes this fairly easy. In particular, the approach is to compute the .intersection
(i.e. overlap) between the boundary of your polygon, and a vertical line at your chosen x-location.
A key thing to note here is that shapely polygons are filled objects, such that computing the overlap of these polygons with a line will return the segment(s) of the line that are inside the polygon. Therefore, to get the points where the vertical line crosses the edge of the polygon, it's probably simplest to compute the intersection between the .boundary
attribute of a polygon and the vertical line.
Implementation
Here is a function returns the intersection points of an input polygon with a vertical line at a specified x value:
import shapely.geometry as sg
def polygon_intersect_x(poly, x_val):
"""
Find the intersection points of a vertical line at
x=`x_val` with the Polygon `poly`.
"""
if x_val < poly.bounds[0] or x_val > poly.bounds[2]:
raise ValueError('`x_val` is outside the limits of the Polygon.')
if isinstance(poly, sg.Polygon):
poly = poly.boundary
vert_line = sg.LineString([[x_val, poly.bounds[1]],
[x_val, poly.bounds[3]]])
pts = [pt.xy[1][0] for pt in poly.intersection(vert_line)]
pts.sort()
return pts
Example Usage
First create an example shapely.geometry.Polygon
:
p = sg.Polygon([(0, 0),
(10, 0),
(30, 10),
(70, -50),
(80, 30),
(40, 40),
(60, 80),
(50, 100),
(15, 20),
(0, 0)])
Note that you can also create shapely objects by:
a) wrapping NumPy arrays to shapely types,
b) Converting MPL paths to shapely polygons as p = sg.Polygon(my_path.vertices)
.
The function above can now be used to calculate min/max intersection points as:
x_val = 45
points = polygon_intersect_x(p, x_val)
minmax = [points[0], points[-1]] # The values are already sorted
print minmax
# [-12.5, 88.5714286]
Here's a simple plot that demonstrates the approach:
import matplotlib.pyplot as plt
ax = plt.gca()
ax.fill(*p.boundary.xy, color='y')
ax.axvline(x_val, color='b')
ax.plot([x_val, ] * len(points), points, 'b.')
ax.plot([x_val, ] * 2, minmax, 'ro', ms=10)
