I have several lines (shapely.geometry.LineString
) and patches (shapely.geometry.Polygon
). My goal is to find lines intersecting a set polygon (a patch). For the moment this works well but I get the entire list of coordinates of the line. Instead I would like to get the two endpoints of the segment that effectively intersect the patch.
The lines are indeed composed of two set of coordinates x
and y
that are lists of floats. They represent [x1, x2, x3, ...]
and [y1, y2, y3, ...]
. Below is a minimal working example to generate two lines and a patch that reproduces the data structure I use in my code (a big class too complex to copy and paste here).
from shapely.geometry import LineString, Polygon
import matplotlib.pyplot as plt
intersections_list = []
x_line_patch = [0, 4, -2, 8, 3, 10, 5]
y_line_patch = [21, 17, 14, 11, 9, 6, 1]
x_line_patch2 = [x + 20 for x in x_line_patch]
y_line_patch2 = y_line_patch
# Create a patch
start_x = 5
end_x = 8
start_y = 5
end_y = 8
polygon = Polygon([(start_x, start_y), (end_x, start_y), (end_x, end_y), (start_x, end_y)])
lines = [LineString(zip(x_line_patch, y_line_patch)), LineString(zip(x_line_patch2, y_line_patch2))]
# Find line intersecting patches
patch_intersecting_lines = [line.xy for line in lines if line.intersects(polygon)]
# Extract the x and y coordinates of the polygon exterior
x, y = polygon.exterior.xy
# Plot the polygon
plt.plot(x, y)
plt.fill(x, y, alpha=0.3) # Fill the polygon with color (optional)
# Plotting the line
x, y = lines[0].xy
x2, y2 = lines[1].xy
plt.plot(x, y)
plt.plot(x2, y2)
plt.tight_layout()
plt.show()
Additionally, creating individual LineString
for each set of (x1, x2), (y1, y2)
would add complexity in a program that already uses a lot of loops and I don't have access to these lists of coordinates in reality. *They're defined here to construct a LineString
object but are created later in the program when the lines are selected if they cross a patch. Otherwise I would just do:
line1 = [LineString(zip(x_line_patch[i:i+2], y_line_patch[i:i+2])) for i in range(0,len(x_line_patch)-1)]
to select coordinates 2 by 2 and create small segments for each line. But in my actual code I do patch_intersecting_lines = [line.xy for line in lines if line.intersects(patch_shape)]
to find intersecting line
given a list of lines called lines
.
Alright, I ended up doing several loop:
[line for line in lines if line.intersects(patch_shape)]
and keep them as LineString
objects for line in patch_intersecting_lines:
x_line, y_line = line.xy
segments = [LineString(zip(x_line[i:i+2], y_line[i:i+2])) for i in range(0, len(x_line) - 1)]
lines_intersecting
list if the segment is intersecting the patch or not (it can give a list of segments)segments_intersecting = [([self.voltage_to_coord_x(x) - patch_x for x in segment.xy[0]],
[self.voltage_to_coord_x(y) - patch_y for y in segment.xy[1]])
for segment in segments if segment.intersects(patch_shape)]
I also calculate the coordinates accordingly because the patch and the line don't have same origin on the diagram (plot vs imshow problem).
It feels overkill since I grab the line get the x
and y
but also re-use them to create the individual segments to then iterate again through the segments to finally return the x
and y
of said segment.
If you have a better suggestion that involves less loop I will gladly accept it.