python scripting polygon point

Python: verificando si el punto está dentro de un polígono



scripting polygon (3)

Tengo una clase que describe un punto (tiene 2 coordenadas xey) y una clase que describe un polígono que tiene una lista de puntos que corresponden a esquinas (autocoronas). Necesito verificar si un punto está en un polígono.

Aquí está la función que se supone que verifica si el punto en el polígono. Estoy usando el método 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

Ejecuto una prueba con la siguiente forma y punto:

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

La secuencia de comandos felizmente devuelve falso aunque el punto dentro de la línea. No puedo encontrar el error


Me gustaría sugerir algunos otros cambios allí:

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

Notas:

  • Un polígono con 5 esquinas también tiene 5 líneas de delimitación, no 6, su ciclo está desactivado.
  • El uso de una expresión de generador por separado deja en claro que está revisando cada línea por turno.
  • Se agregó la verificación de un número vacío de líneas. Sin embargo, la forma de tratar líneas y polígonos de longitud cero con una sola esquina todavía está abierta.
  • También consideraría hacer que las líneas () funcionen como un miembro normal en lugar de una utilidad anidada.
  • En lugar de las muchas estructuras anidadas si, también puede verificar la inversa y luego continue o usar and .

Pasos:

  • Iterato sobre todos los segmentos en el polígono
  • Comprueba si se cruzan con un rayo que va en dirección creciente x

Usando la función 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

Tenga en cuenta que el parámetro inf debe ser el punto máximo en el eje x en su figura.


Sugeriría usar la clase 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))

(También hay una función contains_points si desea probar varios puntos)