Espessura mínima da parede de um polígono não convexo com furos


9

Qual é a maneira mais eficiente de encontrar a espessura mínima da parede (valor e localização) de uma área complexa e não convexa de polígonos, incluindo orifícios? Veja o exemplo de um polígono em azul, com a espessura mínima da parede em vermelho, embora neste caso a localização seja ambígua, se as duas linhas adjacentes forem paralelas.

insira a descrição da imagem aqui

Até agora, tentamos:

  • Subdividir linhas de polígono e encontrar linhas de ponto de ponto mínimo dentro do polígono (força bruta, não eficiente para polígonos complexos com> 10.000 pontos)
  • Triangulação de Delaunay e localização de arestas mínimas dentro do polígono. Não é preciso o suficiente, apenas possível se combinado com a subdivisão das linhas poligonais primeiro. Aqui está um exemplo (Nr 3), em que a triangulação de Delaunay não encontraria bordas simples em vermelho, mas perderia a espessura mínima da parede na caixa verde:
    insira a descrição da imagem aqui
  • Aumento iterativo do buffer de erosão para encontrar a inserção mínima, onde o polígono da erosão se divide em várias partes = metade da espessura mínima da parede. O problema é encontrar a localização da espessura mínima da parede com essa abordagem posteriormente. Além disso, a erosão nem sempre quebra em várias partes e perde "becos sem saída". Aqui está um exemplo (Nr 2) que corroer para uma linha e fornecer a espessura mínima incorreta da parede:
    insira a descrição da imagem aqui
  • Localizando primeiro o eixo medial, procure o círculo mínimo no eixo medial que está cobrindo, mas não sobrepondo, a área do polígono. Edit: problemáticos são os muitos "candidatos errados" no eixo medial: por exemplo. (Nr 1) o círculo A estaria errado, o círculo B indicaria a espessura mínima correta da parede:
    insira a descrição da imagem aqui

Obtenha a distância entre todos os pares de linhas para encontrar os mais próximos.
bugmenot123

Então, o que havia de errado com a abordagem do eixo medial?
Hornbydd

1
@Hornbydd: O problema era que existem muitos círculos no eixo medial que tocam nos cantos, mas não definem a espessura da parede. Veja o segundo exemplo : o círculo A estaria errado, o círculo B seria o local correto da espessura mínima da parede. Então olhares Eixo Central como um desvio caro compuational e não fornecer a resposta certa ...
Oliver Staubli

1
Se você fizer erosão até o polígono degenerar em dois polígonos tocando em um ponto, o local será onde um círculo de raio igual ao buffer central centrado no ponto toca o polígono original. Essa é uma hipótese apresentada sem prova, mas não consigo ver um contra-exemplo ...
Spacedman

1
@OliverStaubli Minha sugestão é verificar não apenas as arestas dos triângulos de Delaunay, mas também as alturas dos triângulos que têm uma aresta no limite e os outros dois no interior do polígono. No exemplo Nr.3, a altura do triângulo sob o quadrado verde é o que você está procurando. (dependendo das restrições da triangulação pode ser necessário também filtrar alguns dos candidatos em triângulos obtusos)
mkadunc

Respostas:


1

Um dos métodos mais eficientes para encontrar a espessura mínima da parede (valor e localização) de uma área complexa de polígonos não convexos, incluindo orifícios, poderia ser usando uma camada regularmente espaçada (ou aleatória) de pontos para determinar o primeiro segmento mais próximo com contexto para cada ponto e, a seguir, o ponto de interseção entre o segmento incremental e o polígono do lado oposto; baseado em diretores cossenos.

Distâncias incrementais podem ser usadas até o primeiro segmento atingir e cruzar algum polígono lateral (a espessura mínima da parede).

Para experimentar minha abordagem, clonei seu polígono com furos e criei uma camada de pontos aleatórios dentro do polígono com 100 pontos; como pode ser observado na seguinte imagem:

insira a descrição da imagem aqui

O código usado do PyQGIS é o seguinte:

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

e produz uma camada de memória de distâncias incrementais (apenas para fins de visualização) e imprime uma espessura mínima da parede no formato WKT.

Depois de executar o código no Python Console do QGIS, obtive resultado da seguinte imagem:

insira a descrição da imagem aqui

Pode-se observar que apenas uma distância incremental alcançou o lado oposto primeiro na área esperada.

O formato WKT impresso (para espessura mínima da parede) é usado com o plug-in QuickWKT do QGIS para visualizar esse segmento na seguinte imagem:

insira a descrição da imagem aqui

A leve inclinação foi produzida porque "o segmento mais próximo ao contexto" foi associado a um vértice; polígono lateral. No entanto, isso pode ser evitado com uma exceção de código ou mais pontos.


1

Mais duas idéias para jogar no pote:

  1. Rasterize seu polígono e use uma transformação de distância (retorna a imagem da distância mais curta de cada pixel diferente de zero para um pixel zero). Esqueletize sua imagem rasterizada e, em seguida, calcule os valores da imagem transformada à distância ao longo do esqueleto. Nesse conjunto, você terá alguns mínimos que devem corresponder à sua largura mais estreita. Esse conjunto pode ser usado como pontos de pesquisa inicial para implementar sua abordagem de força bruta. Devo observar que o esqueleto dividirá os cantos dos objetos e, nesses locais, a transformação de distância se aproximará de zero (à medida que você se aproxima do limite do objeto). Isso pode ser problemático, mas representa um problema com o seu problema - por que ' t a menor largura está em um canto (e é essencialmente zero)? Você pode solucionar esse problema definindo um limite na menor distância ao redor do perímetro entre os dois pontos (se eles estiverem no mesmo objeto). Você pode usar uma transformação de distância geodésica no conjunto de pixels de perímetro para encontrar rapidamente esse valor.

    Esse método exige que você tome uma decisão sobre a resolução do polígono rasterizado, o que introduz alguma dependência de escala. E se você escolher uma resolução muito alta, a transformação da distância poderá consumir tempo. Mas geralmente são bem rápidos. Esse método pode não fornecer a precisão desejada, mas pelo menos pode fornecer um conjunto muito menor de locais que precisam ser verificados.

  2. Seu método de força bruta não é um mau lugar para começar. Eu tive um problema semelhante em que tive que encontrar todas as interseções de uma linha (longa) consigo mesma e consegui acelerar bastante o tempo de pesquisa usando um algoritmo de pesquisa em árvore kd (usei o rangeearch no Matlab na época) para encontrar pontos dentro de um bairro primeiro. Dessa forma, você está forçando brutalmente um pequeno subconjunto do número total de pontos.


Obrigado @jon. Ambas as abordagens promissoras. Eu estava considerando kdTree, mas já esperava que o problema descrito tem um bem conhecido "copiar-colar" solução :-) terá que cavar mais profundo ...
Oliver Staubli

0

A abordagem do eixo medial está correta, você só precisa de um critério para ignorar os círculos defeituosos: Cada círculo no eixo medial toca a superfície em dois (ou mais) pontos. Imagine vetores do centro do círculo até esses pontos na superfície e observe que o ângulo entre é 180 ° para o círculo bom B e apenas 90 ° para o círculo ruim A.

insira a descrição da imagem aqui


0

Um algoritmo de desenho de polígono genérico funciona classificando os segmentos de linha de cima para baixo e trabalhando neles para desenhar linhas ortogonais de pixels. Como essa maneira de quebrar polígonos (sem curvaturas) é muito rápida, pode ser usada como base. Em vez de ir de cima para baixo, você pode ir 0, 30, 60 e 90 graus e encontrar sua seção ortogonal mais curta (= espessura mínima da parede!), Você só precisa calcular isso uma vez por ponto e não para qualquer tipo de 'resolução de pixel'.

Consulte algoritmo de preenchimento de gráficos de linha de computador com polígonos de preenchimento

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.