Espesor mínimo de pared de un polígono no convexo con agujeros

9

¿Cuál es la forma más eficiente de encontrar el grosor mínimo de pared (valor y ubicación) de un área compleja de polígono no convexo que incluye agujeros? Vea el ejemplo de un polígono en azul, con el mínimo espesor de pared en rojo, aunque en este caso la ubicación es ambigua, si las dos líneas adyacentes son paralelas.

ingrese la descripción de la imagen aquí

Hasta ahora, hemos intentado:

  • Subdividir líneas de polígono y encontrar líneas de punto mínimo dentro del polígono (fuerza bruta, no eficiente para polígonos complejos con> 10'000 puntos)
  • Delaunay triangulación y encontrar bordes mínimos dentro del polígono. No es lo suficientemente preciso, solo factible si se combina primero con la subdivisión de líneas poligonales. Aquí está el ejemplo (Nr 3), donde la triangulación de Delaunay no encontraría bordes simples en rojo pero se perdería el mínimo espesor de pared en el cuadro verde:
    ingrese la descripción de la imagen aquí
  • Aumento iterativo del tampón de erosión para encontrar la inserción mínima, donde el polígono de erosión se rompe en varias partes = la mitad del espesor mínimo de la pared. El problema es encontrar la ubicación del espesor de pared mínimo con este enfoque después. Además, la erosión no siempre se rompe en múltiples partes y pierde "callejones sin salida". Aquí hay un ejemplo (Nr 2) que se erosiona en una línea y da el grosor de pared mínimo incorrecto:
    ingrese la descripción de la imagen aquí
  • Primero encuentre el eje medial, luego busque el círculo mínimo en el eje medial que cubre pero no superpone el área del polígono. Editar: problemáticos son los muchos "candidatos equivocados" en el eje medial: por ejemplo. (Nr 1) el círculo A estaría mal, el círculo B indicaría el grosor mínimo correcto de la pared:
    ingrese la descripción de la imagen aquí
Oliver Staubli
fuente
Obtenga la distancia entre todos los pares de líneas para encontrar los más cercanos.
bugmenot123
Entonces, ¿qué estaba mal con el enfoque del eje medial?
Hornbydd
1
@ Hornbydd: El problema era que hay muchos círculos en el eje medial que tocan las esquinas pero no definen el grosor de la pared. Vea el segundo ejemplo : el círculo A estaría mal, el círculo B sería la ubicación correcta del grosor mínimo de la pared. Entonces el eje medial parece un desvío computacional costoso y no proporciona la respuesta correcta ...
Oliver Staubli
1
Si erosiona hasta que el polígono se degenere en dos polígonos que se tocan en un punto, entonces la ubicación será donde un círculo de radio igual al búfer centrado en el punto toque el polígono original. Esa es una hipótesis presentada sin prueba, pero no puedo ver un contraejemplo ...
Spacedman
1
@OliverStaubli Mi sugerencia es verificar no solo los bordes de los triángulos delaunay, sino también las alturas de esos triángulos que tienen un borde en el límite y los otros dos en el interior del polígono. En el ejemplo Nr.3, la altura del triángulo debajo del cuadrado verde es lo que estás buscando. (dependiendo de las limitaciones de la triangulación es posible que necesite también filtrar algunos de los candidatos en triángulos obtusos)
mkadunc

Respuestas:

1

Uno de los métodos más eficientes para encontrar el espesor mínimo de pared (valor y ubicación) de un área de polígono compleja y no convexa que incluye agujeros, podría ser mediante el uso de una capa regularmente espaciada (o aleatoria) de puntos para determinar, primero, el segmento más cercano con contexto para cada punto y, luego, el punto de intersección entre el segmento incremental y el polígono del lado opuesto; basado en directores cosenos.

Las distancias incrementales se pueden usar hasta que el primer segmento alcance e intersecte algún polígono lateral (el grosor mínimo de la pared).

Para probar mi enfoque, cloné su polígono con agujeros y creé una capa de puntos aleatorios dentro del polígono con 100 puntos; como se puede observar en la siguiente imagen:

ingrese la descripción de la imagen aquí

El código usado de PyQGIS tiene el siguiente aspecto:

import math

def azimuth(point1, point2):
   return point1.azimuth(point2) #in degrees

def cosdir_azim(azim):
   azim = math.radians(azim)
   cosa = math.sin(azim)
   cosb = math.cos(azim)
   return cosa,cosb

registry = QgsMapLayerRegistry.instance()    
polygon = registry.mapLayersByName('polygon_with_holes')
point_layer = registry.mapLayersByName('Random_points')
points = [ feat.geometry().asPoint() for feat in point_layer[0].getFeatures() ]
feat_polygon = polygon[0].getFeatures().next()

#producing rings polygons
rings_polygon = feat_polygon.geometry().asPolygon()
segments = []
epsg = point_layer[0].crs().authid()
uri = "LineString?crs=" + epsg + "&field=id:integer""&index=yes"
mem_layer = QgsVectorLayer(uri,
                           'increasing_segments',
                           'memory')
prov = mem_layer.dataProvider()
length = 10
pt2 = 0 
k = 0
while pt2 == 0:
    for i, point in enumerate(points):
        #determining closest distance to vertex or side polygon
        dist1 = feat_polygon.geometry().closestSegmentWithContext(point)[0]
        #determining point with closest distance to vertex or side polygon
        pt = feat_polygon.geometry().closestSegmentWithContext(point)[1]
        cosa, cosb = cosdir_azim(azimuth(pt, point))
        #extending segment in opposite direction based in director cosine and length 
        op_pt  = QgsPoint(point.x() + (length*cosa), point.y() + (length*cosb))
        segments.append([pt,op_pt])
        geom = QgsGeometry.fromPolyline([point,op_pt])
        for ring in rings_polygon:
            geom_ring = QgsGeometry.fromPolyline(ring)
            if geom.intersects(geom_ring):
                pt3 = geom.intersection(geom_ring)
                pt2 = pt3.distance(QgsGeometry.fromPoint(point))
                ms = [pt3.asPoint(), pt]
    length += 100
    k += 1
new_segments = segments[len(segments) -1 - len(segments)/k: len(segments) - 1]

feats = [ QgsFeature() for i in range(len(new_segments)) ]
for i,feat in enumerate(feats):
    feat.setAttributes([i])
    geom = QgsGeometry.fromPolyline(new_segments[i])
    feat.setGeometry(geom)

prov.addFeatures(feats)
QgsMapLayerRegistry.instance().addMapLayer(mem_layer)
minimum_segment = QgsGeometry().fromPolyline(ms).exportToWkt()
print minimum_segment, k

y produce una capa de memoria de distancias incrementales (solo para fines de visualización) e imprime un grosor de pared mínimo en formato WKT.

Después de ejecutar el código en Python Console de QGIS obtuve el resultado de la siguiente imagen:

ingrese la descripción de la imagen aquí

Se puede observar que solo una distancia incremental alcanzó primero el lado opuesto en el área esperada.

El formato WKT impreso (para un grosor de pared mínimo) se usa con el complemento QuickWKT de QGIS para visualizar ese segmento en la siguiente imagen:

ingrese la descripción de la imagen aquí

La ligera inclinación se produjo porque el "segmento más cercano con el contexto" se unió a un vértice; en cambio lado polígono. Sin embargo, se puede evitar con una excepción de código o más puntos.

xunilk
fuente
1

Dos ideas más para tirar en la olla:

  1. Rasterice su polígono y use una transformación de distancia (devuelve la imagen de la distancia más corta desde cada píxel distinto de cero a un píxel cero). Esqueletice su imagen rasterizada, luego tome los valores de la imagen transformada a lo largo del esqueleto. De ese conjunto tendrá algunos mínimos que deberían corresponder a su ancho más estrecho. Este conjunto se puede utilizar como puntos de búsqueda iniciales para luego implementar su enfoque de fuerza bruta. Debo señalar que el esqueleto bisecará las esquinas de los objetos, y en esos lugares la transformación de distancia se acercará a cero (a medida que te acerques al límite del objeto). Esto puede ser problemático, pero representa un problema con su problema: ¿por qué puede ' ¿El ancho más pequeño estará en una esquina (y será esencialmente cero)? Puede resolver este problema estableciendo un umbral en la distancia más corta alrededor del perímetro entre los dos puntos (si se encuentran en el mismo objeto). Puede usar una transformación de distancia geodésica en el conjunto de píxeles del perímetro para encontrar rápidamente ese valor.

    Este método requiere que tome una decisión sobre la resolución del polígono rasterizado, que introduce cierta dependencia de escala. Y si elige una resolución demasiado alta, la transformación de distancia puede llevar mucho tiempo. Pero en general son bastante rápidos. Es posible que este método no le brinde la precisión que desea, pero al menos podría brindarle un conjunto mucho más pequeño de ubicaciones que deben verificarse.

  2. Su método de fuerza bruta no es un mal lugar para comenzar. Tuve un problema similar cuando tuve que encontrar todas las intersecciones de una línea (larga) consigo mismo, y pude acelerar enormemente el tiempo de búsqueda usando un algoritmo de búsqueda de árbol kd (usé rangesearch en Matlab en ese momento) para encontrar puntos dentro de un barrio primero. De esa manera, solo estás forzando a un bruto un pequeño subconjunto del número total de puntos.

Jon
fuente
Gracias @ Jon. Ambos enfoques prometedores. Ya estaba considerando kdTree, pero esperaba que el problema descrito tuviera una solución bien conocida de "copiar y pegar" :-) Tendré que profundizar ...
Oliver Staubli
0

El enfoque del eje medial es correcto, solo necesita un criterio para ignorar los círculos incorrectos: cada círculo en el eje medial toca la superficie en dos (o más) puntos. Imagine vectores desde el centro del círculo hasta estos puntos en la superficie y observe que el ángulo intermedio es 180 ° para el círculo bueno B y solo 90 ° para el círculo malo A.

ingrese la descripción de la imagen aquí

Geom
fuente
0

Un algoritmo genérico de dibujo poligonal funciona ordenando los segmentos de línea de arriba a abajo y trabajando a través de ellos para dibujar líneas ortogonales de píxeles. Como esta forma de descomponer polígonos (sin curvaturas) es muy rápida, puede usarse como base. En lugar de ir solo de arriba a abajo, puede ir a 0, 30, 60 y 90 grados, y encontrar su sección ortogonal más corta (= ¡espesor de pared mínimo!), Solo tiene que calcular eso una vez por punto y no para cualquier tipo de 'resolución de píxeles'.

Ver computer-graphics-scan-line-polygon-fill-Algoritmo

usuario3042469
fuente