Reputation: 325
I am trying to find the intersections between circular polygons and a line and then merge the results.
I would expect all the intersections to belong to the line and also their union, but it is not what I see.
Here an example:
from shapely.geometry import LineString, Point
line = LineString([(2, 5), (2.1, 1)]) # slightly oblique line
point1 = Point(2.5, 3)
int1 = line.intersection(point1.buffer(1))
int2 = line.intersection(point1.buffer(1.3))
union = int1.union(int2)
print union.length
print int2.length
results:
4.22183534925
2.43702622444
since int2 contains int1 I would expect their union to be exactly int2. I would expect also the union to be just a LineString, but instead it is a MultiLineString composed by 4 lines.
If I plot the union lines I see how their are really close each other but not on the same line.
I think it is something to do with the resolution of the shapely objects values.
Any suggestion how to merge "almost parallel" lines into one? Or do you suggest any other solution to this problem?
Upvotes: 1
Views: 7073
Reputation: 325
Since I am mainly interested in the length of the union rather then its exact vertices I have found another way to measure it.
from shapely.geometry import LineString, Point
line = LineString([(2, 5), (2.1, 1)]) # slightly oblique line
point1 = Point(2.5, 3)
int1 = line.intersection(point1.buffer(1))
int2 = line.intersection(point1.buffer(1.3))
line_not_intersected = line.difference(point1.buffer(1))
line_not_intersected = line_not_intersected.difference(point1.buffer(1.3))
print line.length - line_not_intersected.length
print int2.length
and it gives the expected result:
2.43702622444
2.43702622444
Interesting, and annoying at the same time, if I get the union as
union = line.difference(line_not_intersected)
union is a LineString, but its length is not the same as the difference of the lengths of the two lines.
print line.length - line_not_intersected.length
print union.length
2.43702622444
4.00124980475
Upvotes: 0
Reputation: 3954
As far as I know, there is no shapely built-in function to do what you need. Besides, as you noticed, ther are some round errors that you need to address. Fortunately, there are some shapely functions that can help you to implement a short solution:
project
method of a LineString that takes a point and gives you the distance from the first point of the line to the closest point of the line to the given point.interpolate
method of a LineString that given a distance, it gives you the point that is in the LineString with that distance from the first point of the line. I will assume you know what line is inside the other, as in the example. If this is the case, what you can do is to projet the points of the shorter line into the longer line, and then sort those points considering their distance to the first point of the line.
With the following code,
from shapely.geometry import LineString, Point
from shapely.ops import linemerge
def getProyection(aline, point_coords):
return aline.interpolate(aline.project(Point(point_coords)))
def mergeInside(aline, inside_line):
mline_tups = [(aline.project(Point(p)), p) for p in aline.coords]
mline_tups.extend([ (aline.project(Point(p)), getProyection(aline, p))
for p in inside_line.coords])
mline_tups.sort()
return LineString([p for _, p in mline_tups])
line = LineString([(2, 5), (2.1, 1)]) # slightly oblique line
point1 = Point(2.5, 3)
int1 = line.intersection(point1.buffer(1))
int2 = line.intersection(point1.buffer(1.3))
merged_line = mergeInside(int2, int1)
print(int1)
print(int2)
print(merged_line)
I get:
LINESTRING (2.02796158358983 3.881536656406788, 2.072567874421353 2.097285023145893)
LINESTRING (2.019819096605441 4.207236135782384, 2.080725721869193 1.770971125232286)
LINESTRING (2.019819096605441 4.207236135782384, 2.027961583589831 3.881536656406788, 2.072567874421353 2.097285023145893, 2.080725721869193 1.770971125232286)
Upvotes: 1