使用matplotlib解决Polygone中的Point

时间:2016-09-14 09:39:15

标签: python algorithm python-2.7 matplotlib point-in-polygon

我正在寻找一种算法来检查一个点是否在多边形内。

我目前正在使用mplPath和contains_point()但它似乎在某些情况下无效。

编辑2016年9月16日:

好吧所以我通过简单地检查边缘上的点是否也改进了我的代码。我仍然对矩形和蝴蝶结示例有一些问题:

新代码:

#for PIP problem
import matplotlib.path as mplPath
import numpy as np
#for plot
import matplotlib.pyplot as plt


def plot(poly,points):
    bbPath = mplPath.Path(poly)
    #plot polygon
    plt.plot(*zip(*poly))

    #plot points
    xs,ys,cs = [],[],[]
    for point in points:
        xs.append(point[0])
        ys.append(point[1])
        color = inPoly(poly,point)
        cs.append(color)
        print point,":", color
    plt.scatter(xs,ys, c = cs , s = 20*4*2)

    #setting limits
    axes = plt.gca()
    axes.set_xlim([min(xs)-5,max(xs)+50])
    axes.set_ylim([min(ys)-5,max(ys)+10])

    plt.show()

def isBetween(a, b, c): #is c between a and b ?
    crossproduct = (c[1] - a[1]) * (b[0] - a[0]) - (c[0] - a[0]) * (b[1] - a[1])
    if abs(crossproduct) > 0.01 : return False   # (or != 0 if using integers)

    dotproduct = (c[0] - a[0]) * (b[0] - a[0]) + (c[1] - a[1])*(b[1] - a[1])
    if dotproduct < 0 : return False

    squaredlengthba = (b[0] - a[0])*(b[0] - a[0]) + (b[1] - a[1])*(b[1] - a[1])
    if dotproduct > squaredlengthba: return False

    return True

def get_edges(poly):
    # get edges
    edges = []
    for i in range(len(poly)-1):
        t = [poly[i],poly[i+1]]
        edges.append(t)
    return edges

def inPoly(poly,point):
    if bbPath.contains_point(point) == True:
        return 1
    else:
        for e in get_edges(poly):
            if isBetween(e[0],e[1],point):
                return 1
    return 0
# TESTS ========================================================================
#set up poly
polys = {
1 : [[10,10],[10,50],[50,50],[50,80],[100,80],[100,10],[10,10]], # test rectangulary shape
2 : [[20,10],[10,20],[30,20],[20,10]], # test triangle
3 : [[0,0],[0,10],[20,0],[20,10],[0,0]], # test bow-tie
4 : [[0,0],[0,10],[20,10],[20,0],[0,0]] # test rect
}

#points to check
points = {
1 : [(10,25),(50,75),(60,10),(20,20),(20,60),(40,50)], # rectangulary shape test pts
2 : [[20,10],[10,20],[30,20],[-5,0],[20,15]] , # triangle  test pts
3 : [[0,0],[0,10],[20,0],[20,10],[10,0],[10,5],[15,5]],  # bow-tie shape test pts
4 : [[0,0],[0,10],[20,0],[20,10],[10,0],[10,5],[15,5]]  # rect shape test pts
}

#print bbPath.contains_points(points) #0 if outside, 1 if inside
for data in zip(polys.itervalues(),points.itervalues()):
    plot(data[0],data[1])

新代码的输出:

enter image description here

旧代码:

#for PIP problem
import matplotlib.path as mplPath
import numpy as np
#for plot
import matplotlib.pyplot as plt

#set up poly
array = np.array([[10,10],[10,50],[50,50],[50,80],[100,80],[100,10]])
bbPath = mplPath.Path(array)

#points to check
points = [(10,25),(50,75),(60,10),(20,20),(20,60),(40,50)]


print bbPath.contains_points(points) #0 if outside, 1 if inside

#plot polygon
plt.plot(*zip(*array))

#plot points
xs,ys,cs = [],[],[]
for point in points:
    xs.append(point[0])
    ys.append(point[1])
    cs.append(bbPath.contains_point(point))
plt.scatter(xs,ys, c = cs)

#setting limits
axes = plt.gca()
axes.set_xlim([0,120])
axes.set_ylim([0,100])

plt.show()

我想出了以下graph。你可以看到红色包围的三个点被指示为在多边形之外(蓝色),当我希望它们在里面时。

我也尝试更改路径bbPath.contains_points(points, radius = 1.)的半径值,但这没有任何区别。

欢迎任何帮助。

编辑:

来自此问题答案中提出的算法的

enter image description here屏幕截图似乎表明它在其他情况下失败了。

2 个答案:

答案 0 :(得分:1)

好的,所以我终于设法使用匀称来完成它。

#for PIP problem
import matplotlib.path as mplPath
import numpy as np
#for plot
import matplotlib.pyplot as plt
import shapely.geometry as shapely

class MyPoly(shapely.Polygon):
    def __init__(self,points):
        super(MyPoly,self).__init__(points)
        self.points = points
        self.points_shapely = [shapely.Point(p[0],p[1]) for p in points]

def convert_to_shapely_points_and_poly(poly,points):
    poly_shapely = MyPoly(poly)
    points_shapely = [shapely.Point(p[0],p[1]) for p in points]
    return poly_shapely,points_shapely

def plot(poly_init,points_init):
    #convert to shapely poly and points
    poly,points = convert_to_shapely_points_and_poly(poly_init,points_init)

    #plot polygon
    plt.plot(*zip(*poly.points))

    #plot points
    xs,ys,cs = [],[],[]
    for point in points:
        xs.append(point.x)
        ys.append(point.y)
        color = inPoly(poly,point)
        cs.append(color)
        print point,":", color
    plt.scatter(xs,ys, c = cs , s = 20*4*2)

    #setting limits
    axes = plt.gca()
    axes.set_xlim([min(xs)-5,max(xs)+50])
    axes.set_ylim([min(ys)-5,max(ys)+10])

    plt.show()


def isBetween(a, b, c): #is c between a and b ?
    crossproduct = (c.y - a.y) * (b.x - a.x) - (c.x - a.x) * (b.y - a.y)
    if abs(crossproduct) > 0.01 : return False   # (or != 0 if using integers)

    dotproduct = (c.x - a.x) * (b.x - a.x) + (c.y - a.y)*(b.y - a.y)
    if dotproduct < 0 : return False

    squaredlengthba = (b.x - a.x)*(b.x - a.x) + (b.y - a.y)*(b.y - a.y)
    if dotproduct > squaredlengthba: return False

    return True

def get_edges(poly):
    # get edges
    edges = []
    for i in range(len(poly.points)-1):
        t = [poly.points_shapely[i],poly.points_shapely[i+1]]
        edges.append(t)
    return edges


def inPoly(poly,point):
    if poly.contains(point) == True:
        return 1
    else:
        for e in get_edges(poly):
            if isBetween(e[0],e[1],point):
                return 1
    return 0


# TESTS ========================================================================
#set up poly
polys = {
1 : [[10,10],[10,50],[50,50],[50,80],[100,80],[100,10],[10,10]], # test rectangulary shape
2 : [[20,10],[10,20],[30,20],[20,10]], # test triangle
3 : [[0,0],[0,10],[20,0],[20,10],[0,0]], # test bow-tie
4 : [[0,0],[0,10],[20,10],[20,0],[0,0]], # test rect clockwise
5 : [[0,0],[20,0],[20,10],[0,10],[0,0]] # test rect counter-clockwise
}

#points to check
points = {
1 : [(10,25),(50,75),(60,10),(20,20),(20,60),(40,50)], # rectangulary shape test pts
2 : [[20,10],[10,20],[30,20],[-5,0],[20,15]] , # triangle  test pts
3 : [[0,0],[0,10],[20,0],[20,10],[10,0],[10,5],[15,5]],  # bow-tie shape test pts
4 : [[0,0],[0,10],[20,0],[20,10],[10,0],[10,5],[15,2],[30,8]],  # rect shape test pts
5 : [[0,0],[0,10],[20,0],[20,10],[10,0],[10,5],[15,2],[30,8]]  # rect shape test pts
}

#print bbPath.contains_points(points) #0 if outside, 1 if inside
for data in zip(polys.itervalues(),points.itervalues()):
    plot(data[0],data[1])

答案 1 :(得分:0)

如果做到这一点还有很长的路要走,你可以采用这样的原则:如果交叉点的数量是奇数,则一个点位于多边形内部,如果交叉点的数量是偶数,则类似于外部 。这是我在上面给出的测试用例中放入的一些草率的python。

# polygon points: [[x,y],...]
points = [[10,10],[10,50],[50,50],[50,80],[100,80],[100,10]]


# get edges
edges = []
for i in range(len(points)-1):
    t = [points[i],points[i+1]]
    edges.append(t)

# get min and max x values to use as the length of ray
xmax = max(points,key=lambda item:item[1])[0]
xmin = min(points,key=lambda item:item[1])[1]
dist = xmax-xmin

# return True if p1,p2,p3 are on the same line
def colinear(p1,p2,p3):
      return (p1[0]*(p2[1] - p3[1]) + p2[0]*(p3[1] - p1[1]) + p3[0]*(p1[1] - p2[1])) == 0

# return True if p1 is on the line segment p2-p3
def inRange(p1,p2,p3):
    dx = abs(p3[0]-p2[0])
    dy = abs(p3[1]-p2[1])
    if abs(p3[0]-p1[0])+abs(p2[0]-p1[0])==dx and abs(p3[1]-p1[1])+abs(p2[1]-p1[1])==dy:
        return True
    return False


# line segment intersection between
# (x1,y1)-(x1+dist,y1) and 
# (x3,y3)-(x4,y4)
def intersect(x1,y1,x3,y3,x4,y4):
    x2 = x1+dist
    B1 = x1-x2
    C1 = B1*y1

    A2 = y4-y3
    B2 = x3-x4
    C2 = A2*x3+B2*y3
    det =-A2*B1
    if(det == 0):
        return False
    x = (B2*C1 - B1*C2)/det
    if inRange((x,y1),(x3,y3),(x4,y4)):
        return True
    return False

# return True if point (x,y) is inside
# polygon defined above
def isInside(x,y):
    i = 0
    for edge in edges:
        # if (x,y) is on the edge return True
        if colinear((x,y),edge[0],edge[1]) and inRange((x,y),edge[0],edge[1]):
            return True
        # if both x values of edge are to the left of (x,y)
        # if both y values of edge are are above or bellow (x,y)
        #    then skip
        if edge[0][0] < x and edge[1][0] < x:
            continue
        if edge[0][1] < y and edge[1][1] < y:
            continue
        # if ray intersects edge, increment i
        if intersect(x,y,edge[0][0],edge[0][1],edge[1][0],edge[1][1]):
            i+=1
    if i%2==1:
        return True
    else:
        return False


l = [(10,25),(50,75),(60,10),(20,20),(20,60),(40,50)]
for p in l:
    print(isInside(p[0],p[1]))