简体   繁体   中英

Shapely: intersection point between line and polygon in 3D

Last time I used shapely, I really had this nice import-and-fly-feeling . However recently, I run into a rather non-intuitive behavior in this module, as I tried to find the intersection between a line segment and a triangle in 3D space. Let's define a segment and a triangle as follow:

l = LineString([[1,0.5,0.5],[3,0.5,0.5]])
p = Polygon([[1.2,0.0,0.],[2.2,1.0,0.],[2.8,0.5,1.]])

To get their point of intersection I used l.intersection(p) , and expected a point, namely POINT Z (POINT Z (2 0.5 0.25)) . It is illustrated with the blue dot below:

在此输入图像描述

Instead what I got was LINESTRING Z (1.7 0.5 0.25, 2.8 0.5 1) - red line below - and frankly I am quite perplexed about what it is supposed to represent. 在此输入图像描述

Oddly enough, when the polygon/triangle is in the xz-plane and orthogonal to the line segment, the function behaves as one would expect. When the triangle is "leaning", however, it returns a line. This has temporarily lead me to believe that it returned the intersection between the line and the triangle's bounding box. The above red line proves otherwise.

So a workaround for this problem has been to read this very enlightening web-page , and adapt their C++ code to work with shapely objects. The intersection method works great to check whether the line goes through the polygon, and the function below finds the point of interest.

def intersect3D_SegmentPlane(Segment, Plane):

    # Points in Segment: Pn  Points in Plane: Qn
    P0, P1     = np.array(Segment.coords)
    Q0, Q1, Q2 = np.array(Plane.exterior)[:-1]   

    # vectors in Plane
    q1 = Q1 - Q0
    q2 = Q2 - Q0

    # vector normal to Plane
    n  = np.cross(q1, q2)/np.linalg.norm(np.cross(q1, q2))
    u = P1 - P0 # Segment's direction vector 
    w = P0 - Q0 # vector from plane ref point to segment ref point

    ## Tests parallelism
    if np.dot(n, u) == 0:
        print "Segment and plane are parallel"
        print "Either Segment is entirely in Plane or they never intersect."
        return None
    ## if intersection is a point
    else:
        ## Si is the scalar where P(Si) = P0 + Si*u lies in Plane
        Si = np.dot(-n, w) / np.dot(n, u)
        PSi = P0 + Si * u
        return PSi

Not very import-and-fly anymore...

So finally to my questions:

  • What does intersection return when applied to 3D-objects and why is it a line?

  • Is there a function in shapely that does what I want? or any optional argument, tweak or dark magic trick?

  • Is there any other libraries out there that would do this job while fulfilling my dreams of simplicity and laziness?

unfortunately, as the documentation states:

Coordinate sequences are immutable. A third z coordinate value may be used when constructing instances, but has no effect on geometric analysis. All operations are performed in the xy plane.

One can verify this with:

from shapely.geometry import LineString, Polygon

l = LineString([[1,0.5,0.5],[3,0.5,0.5]])
p = Polygon([[1.2,0.0,0.],[2.2,1.0,0.],[2.8,0.5,1.]])
print(l.intersection(p))
#LINESTRING Z (1.7 0.5 0.25, 2.8 0.5 1)

l = LineString([[1,0.5],[3,0.5]])
p = Polygon([[1.2,0.0],[2.2,1.0],[2.8,0.5]])
print(l.intersection(p))
#LINESTRING (1.7 0.5, 2.8 0.5)

or even:

from shapely.geometry import LineString, Polygon

l = LineString([[1,0.5,0],[3,0.5,0]])
p = Polygon([[1.2,0.0,1],[2.2,1.0,1],[2.8,0.5,1]])
print(l.intersects(p))
#True (even though the objects are in different z-planes)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM