Uno de los métodos más eficientes para encontrar el espesor de pared mínimo (valor y ubicación) de un área de polígono complejo, no convexo, incluyendo 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 el contexto para cada punto y, a continuación, el punto de intersección entre el segmento incremental y el polígono de lado opuesto; basado en directores cosenos.
Se pueden utilizar distancias incrementales hasta que el primer segmento alcance e intersecte algún polígono lateral (el espesor 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:
El código utilizado por 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 (sólo para fines de visualización) e imprime el espesor mínimo de la pared en formato WKT.
Después de ejecutar el código en la consola de Python de QGIS obtuve el resultado de la siguiente imagen:
Se puede observar que sólo una distancia incremental llegó primero al lado opuesto en el área esperada.
El formato WKT impreso (para el espesor mínimo de la pared) se utiliza con el plugin QuickWKT de QGIS para visualizar ese segmento en la siguiente imagen:
La ligera inclinación se produjo porque el "segmento más cercano con contexto" se unió a un vértice; en lugar de polígono lateral. Sin embargo, se puede evitar con una excepción de código o más puntos.
0 votos
Obtén la distancia entre todos los pares de líneas para encontrar las más cercanas.
0 votos
Entonces, ¿qué es lo que falla en el enfoque del eje medial?
0 votos
Usted quiere que el ubicación y el ancho de la pared mínima?
1 votos
@Hornbydd: El problema era, que hay muchos círculos en el eje medial que están tocando esquinas pero no están definiendo el grosor de la pared. Ver segundo ejemplo El círculo A sería erróneo, el círculo B sería la ubicación correcta del grosor mínimo de la pared. Así que el eje medial parece un desvío computacional costoso y no proporciona la respuesta correcta...
0 votos
@Spacedman: Sí. Usando el apporach de buffer/erosión se utilizó para encontrar el "ancho", pero no la "ubicación".
1 votos
Si haces una erosión hasta que el polígono degenere en dos polígonos que se tocan en un punto, entonces el lugar será donde un círculo de radio igual al del buffer centrado en el punto toque al polígono original. Es una hipótesis presentada sin pruebas, pero no veo un contraejemplo...
0 votos
En una triangulación restringida sin vértices interiores (un poco como cs.cmu.edu/~quake/triangle.defs.html ) podrías encontrar la arista más corta que no está en el límite o la altura más corta de un triángulo cuya base está en el límite.
0 votos
@Spacedman: Gracias por la hipótesis. Véase mi contraejemplo (nº 2) más arriba. Intenté solucionar este problema de "callejón sin salida" invirtiendo la erosión en cada paso y comparando las áreas. Si el área_antes_de_erosión menos el área_después_de_erosión están demasiado lejos, dejo de aumentar la erosión y utilizo el buffer = la mitad del grosor mínimo de la pared. Todavía espero encontrar una solución más sencilla...
0 votos
@mkadunc: Ya lo he hecho. El problema está en la precisión de la subdivisión. Ver ejemplo nº 3. Sólo si subdivido el polígono inicial "lo suficiente", entonces Delaunay encontrará el espesor de pared mínimo como una de las aristas del simplex interior.
0 votos
No estoy seguro de cuál es la "pared mínima" ahora - porque si sus "alas" rectangulares al norte y al este del rectángulo en Ex 2 pueden ser paredes válidas, entonces cualquier esquina externa crea una "pared" con ancho que tiende a cero....
0 votos
@Spacedman: Buen punto sobre las "alas" frente a los "muros". Quizá estemos persiguiendo lo "imposible". Como nuestros polígonos de prueba suelen incluir esas "alas" (más a menudo "alas" que tienen "alas" adyacentes, en forma de L), consideraríamos esas "alas" como paredes. Veo su punto sobre los triángulos externos, así como las esquinas que tienen un espesor que tiende a cero. No sé cómo podríamos excluir estas secciones transversales de esquina no deseadas...
0 votos
Dudo que se pueda responder a esto hasta que se pueda plantear formalmente en términos geométricos. Está claro que tienes una idea de lo que debería ser el "muro mínimo", pero a menos que puedas expresarlo en un lenguaje formal, creo que estamos atascados.
1 votos
@OliverStaubli Mi sugerencia es comprobar no sólo las aristas de los triángulos delaunay, sino también las alturas de aquellos triángulos que tienen una arista en el límite y las otras dos en el interior del polígono. En el ejemplo nº 3 lo que se busca es la altura del triángulo que está debajo del cuadrado verde. (dependiendo de las restricciones de la triangulación puede ser necesario filtrar también algunos de los candidatos en triángulos obtusos)
0 votos
@mkadunc: ¡Gran idea! Esto debería solucionar muchos de los casos en los que teníamos problemas de precisión y teníamos que subdividir más el polígono inicial. Muchas gracias. Publicaré los detalles de nuestra solución final una vez que esté implementada.
0 votos
@Spacedman: Totalmente de acuerdo. Estamos discutiendo internamente los requisitos para formalizar el concepto de "ala" y lo que es exactamente una "pared" (y lo que no). Midiendo manualmente es obvio qué y dónde encontrar el grosor mínimo de pared en un plano CAD, pero traducir esta intuición humana en un algoritmo fiable parece muy difícil...
0 votos
@Spacedman 1) con el eje medial, puede asignar un valor escalar de "distancia al límite" a cada punto del eje - el "grosor mínimo de la pared" podría definirse como el punto en el que este valor alcanza el mínimo local más pequeño (es decir, tal que al moverse a lo largo del eje medial en cualquier dirección no disminuye la distancia al límite).
0 votos
@Spacedman 2) Con el buffer de erosión - podría definir la distancia mínima de la pared como el punto/distancia en el que el polígono colapsa en un punto "puente" (una pequeña vecindad de un punto puente consiste en al menos dos regiones que están conectadas sólo a través de ese punto - en la práctica hay sólo un par de opciones para tal punto - ya sea el punto donde dos polígonos en un multi-polígono se tocan; el punto en el que un agujero toca el límite exterior de un polígono; o cualquier punto de un segmento de línea "colgante" que resulte en la erosión de un segmento perfectamente recto de un polígono (como el nº. 2)