Reputation: 145
I am trying to insert a point from a projected line into a polygon (I need to do this a few times to 'cut' a section of the polygon exterior, but this the key issue).
There is close similarity with this question, but I find that answer does or doesn't work depending on the segment orientation. It works for simple vertical or horizontal projections, but in most other segment orientations results in a fail due to the projected point falling outside the polygon.exterior by ~ 1e-16 (numerical precision).
This question checks for a point being inside the polygon, and also fails when the point is on the polygon exterior.
I have tried extending the projected line (pline
below) by a tiny bit then intersection
, but that gives the exact same Point and numerical precision problem.
Avoiding the precision by adding a tiny .buffer(1e-15)
does intercept, but that results in adding a (tiny) rounded line segment from the buffer which introduces errors elsewhere.
Example:
import matplotlib.pyplot as plt
import shapely as sh
from shapely import ops
from shapely.geometry import *
def insert_projected_point(point, poly):
s = sh.normalize(Polygon(poly))
pline = sh.shortest_line(point,s)
p2 = sh.intersection(s, pline)
# Per https://gis.stackexchange.com/questions/188594/how-can-i-add-points-to-a-linestring-in-shapely
ring = LineString(list(s.exterior.coords))
union = ring.union(pline)
s2 = ops.polygonize(union)[0]
return p2, s2
def do_results(p, s, p1, s1):
print(f"s: {s}")
print(f"p out: {p1}")
print(f"s out: {s1}")
p = Point((3, 0.5))
# This works
poly1 = (0,0), (2,0), (2,2), (0,2), (0,0)
p1, s1 = insert_projected_point(p, poly1)
do_results(p, poly1, p1, s1)
# This doesn't work
poly2 = (0,0), (1,0), (2,2), (0,2), (0,0)
p2, s2 = insert_projected_point(p, poly2)
do_results(p, poly2, p2, s2)
Result:
s: ((0, 0), (2, 0), (2, 2), (0, 2), (0, 0))
p out: POINT (2 0.5)
sout: POLYGON ((0 0, 0 2, 2 2, 2 0.5, 2 0, 0 0))s: ((0, 0), (1, 0),(2, 2), (0, 2), (0, 0))
p out: LINESTRING Z EMPTY
s out: POLYGON((0 0, 0 2, 2 2, 1 0, 0 0))
Upvotes: 2
Views: 452
Reputation: 145
Solution to extend the intersect line by a tiny fraction works now (not sure where I went wrong earlier)
def insert_projected_point(point, poly, scaler=1):
s = sh.normalize(Polygon(poly))
pline = sh.shortest_line(point,s)
# add this line to make it work
pline = sh.affinity.scale(pline, xfact=scaler, yfact=scaler, origin='center')
p2 = sh.intersection(s, pline)
# Per https://gis.stackexchange.com/questions/188594/how-can-i-add-points-to-a-linestring-in-shapely
ring = LineString(list(s.exterior.coords))
union = ring.union(pline)
s2 = ops.polygonize(union)[0]
print(f"p out: {p2}")
print(f"s out: {s2}")
p = Point((3, 0.5))
poly2 = (0,0), (1,0), (2,2), (0,2), (0,0)
# This doesn't work
insert_projected_point(p, poly2)
# p out: LINESTRING Z EMPTY
# s out: POLYGON ((0 0, 0 2, 2 2, 1 0, 0 0))
# This works
insert_projected_point(p, poly2, scaler=1 + 1e-12)
# p out: LINESTRING (1.6 1.2, 1.5999999999992998 1.2000000000003501)
# s out: POLYGON ((0 0, 0 2, 2 2, 1.6 1.2, 1 0, 0 0))
Upvotes: 0