web-dev-qa-db-fra.com

Python: vérifier si le point se trouve à l'intérieur d'un polygone

J'ai une classe décrivant un point (a 2 coordonnées x et y) et une classe décrivant un polygone qui a une liste de points qui correspondent aux coins (self.corners) J'ai besoin de vérifier si un point est dans un polygone

Voici la fonction qui est censée vérifier si le point dans le polygone. J'utilise la méthode Ray Casting

def in_me(self, point):
        result = False
        n = len(self.corners)
        p1x = int(self.corners[0].x)
        p1y = int(self.corners[0].y)
        for i in range(n+1):
            p2x = int(self.corners[i % n].x)
            p2y = int(self.corners[i % n].y)
            if point.y > min(p1y,p2y):
                if point.x <= max(p1x,p2x):
                    if p1y != p2y:
                        xinters = (point.y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                        print xinters
                    if p1x == p2x or point.x <= xinters:
                        result = not result
            p1x,p1y = p2x,p2y
         return result

Je lance un test avec la forme et le point suivants:

PG1 = (0,0), (0,2), (2,2), (2,0)
point = (1,1)

Le script retourne heureusement False même s'il le pointe dans la ligne. Je n'arrive pas à trouver l'erreur

37
Helena

Je suggère d'utiliser la classe Path de matplotlib

import matplotlib.path as mplPath
import numpy as np

poly = [190, 50, 500, 310]
bbPath = mplPath.Path(np.array([[poly[0], poly[1]],
                     [poly[1], poly[2]],
                     [poly[2], poly[3]],
                     [poly[3], poly[0]]]))

bbPath.contains_point((200, 100))

(Il y a aussi contains_points fonction si vous voulez tester plusieurs points)

57
P.R.

Je voudrais suggérer quelques autres changements:

def contains(self, point):
    if not self.corners:
        return False

    def lines():
        p0 = self.corners[-1]
        for p1 in self.corners:
            yield p0, p1
            p0 = p1

    for p1, p2 in lines():
        ... # perform actual checks here

Remarques:

  • Un polygone à 5 coins a également 5 lignes de délimitation, et non 6, votre boucle est unique.
  • L'utilisation d'une expression de générateur distincte indique clairement que vous vérifiez chaque ligne tour à tour.
  • La vérification d'un nombre vide de lignes a été ajoutée. Cependant, la façon de traiter les lignes et les polygones de longueur nulle avec un seul coin est toujours ouverte.
  • J'envisagerais également de faire de la fonction lines () un membre normal au lieu d'un utilitaire imbriqué.
  • Au lieu des nombreuses structures if imbriquées, vous pouvez également vérifier l'inverse puis continue ou utiliser and.
3
Ulrich Eckhardt

Pas:

  • Itérer sur tous les segments du polygone
  • Vérifiez si elles se croisent avec un rayon allant dans la direction x croissante

Utilisation de la fonction intersect de This SO Question

def ccw(A,B,C):
    return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x)

# Return true if line segments AB and CD intersect
def intersect(A,B,C,D):
    return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D)

def point_in_polygon(pt, poly, inf):
    result = False
    for i in range(len(poly.corners)-1):
        if intersect((poly.corners[i].x, poly.corners[i].y), ( poly.corners[i+1].x, poly.corners[i+1].y), (pt.x, pt.y), (inf, pt.y)):
            result = not result
    if intersect((poly.corners[-1].x, poly.corners[-1].y), (poly.corners[0].x, poly.corners[0].y), (pt.x, pt.y), (inf, pt.y)):
        result = not result
    return result

Veuillez noter que le paramètre inf doit être le point maximum sur l'axe des x dans votre figure.

0
dccsillag