Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shapely unable to split line on point due to precision issues

Tags:

python

shapely

I am trying to interpolate a point onto a LineString in Shapely and then split the linestring accordingly. However, due to a precision error, Shapely thinks that the interpolated point is not on the linestring, and therefore the split operation does not work.

Here is an example:

from shapely.ops import split
from shapely.geometry import LineString, Point

### Initialize point and line
line = LineString([(0.123,0.456),(5.678,7.890),(12.135,6.789)])    
point = Point(4.785,8.382)   

### Interpolate point onto line
new_point = line.interpolate(line.project(point))
print new_point
>> POINT (5.593949278213755 7.777518800043393)

### BUT: line does not intersect the interpolated point
line.intersects(new_point)
>> False

### EVEN THOUGH: distance between them is essentially (not exactly) zero
line.distance(new_point)
>> 0.0

### THEREFORE: line cannot be split using the new point
len(split(line, new_point))
>> 1

I think the problem is as follows:
1. I rounded the original point/line coordinates so that they do not run up against the precision limits of the machine.
2. However, the INTERPOLATED point has very high precision. I don't know how to control this.
3. In theory, I could round the coordinates of this new point, but that doesn't seem to ensure that the new point is on the line either.

Related questions here, here, and here.

like image 279
atkat12 Avatar asked May 05 '18 21:05

atkat12


2 Answers

I figured out a somewhat hacky solution. If anyone posts a better one I will accept that instead.

# After the code above: 

### Create a buffer polygon around the interpolated point
buff = new_point.buffer(0.0001)

### Split the line on the buffer
first_seg, buff_seg, last_seg = split(line,buff)

### Stitch together the first segment, the interpolated point, and the last segment 
line = LineString(list(first_seg.coords) + list(new_point.coords) + list(last_seg.coords))

line.intersects(new_point)
>> True
like image 137
atkat12 Avatar answered Nov 19 '22 06:11

atkat12


I tried myself with the code above but sometimes it does fail due to a large number of split results...apparently the polygon created breaks up the line in several points, don't know why.

I used instead the snap function, and it seems to work:

    snap(line_to_modify,closest_point,0.01)

The returned value is the modified geometry, with the closest point in it. Firstly, you need to use project and interpolate to find the closest point not in the line. you can veryfy it then with intersect

like image 3
melezz Avatar answered Nov 19 '22 05:11

melezz