Eu tenho algum código que estou usando para determinar quais polígonos esculturais / MultiPolygons se cruzam com um número de linhas de formas bem torneadas. Através das respostas a esta pergunta, o código passou disso:
import fiona
from shapely.geometry import LineString, Polygon, MultiPolygon, shape
# Open each layer
poly_layer = fiona.open('polygon_layer.shp')
line_layer = fiona.open('line_layer.shp')
# Convert to lists of shapely geometries
the_lines = [shape(line['geometry']) for line in line_layer]
the_polygons = [(poly['properties']['GEOID'], shape(poly['geometry'])) for poly in poly_layer]
# Check for Polygons/MultiPolygons that the LineString intersects with
covered_polygons = {}
for poly_id, poly in the_polygons:
for line in the_lines:
if poly.intersects(line):
covered_polygons[poly_id] = covered_polygons.get(poly_id, 0) + 1
onde todas as interseções possíveis são verificadas, para isso:
import fiona
from shapely.geometry import LineString, Polygon, MultiPolygon, shape
import rtree
# Open each layer
poly_layer = fiona.open('polygon_layer.shp')
line_layer = fiona.open('line_layer.shp')
# Convert to lists of shapely geometries
the_lines = [shape(line['geometry']) for line in line_layer]
the_polygons = [(poly['properties']['GEOID'], shape(poly['geometry'])) for poly in poly_layer]
# Create spatial index
spatial_index = rtree.index.Index()
for idx, poly_tuple in enumerate(the_polygons):
_, poly = poly_tuple
spatial_index.insert(idx, poly.bounds)
# Check for Polygons/MultiPolygons that the LineString intersects with
covered_polygons = {}
for line in the_lines:
for idx in list(spatial_index.intersection(line.bounds)):
if the_polygons[idx][1].intersects(line):
covered_polygons[idx] = covered_polygons.get(idx, 0) + 1
onde o índice espacial é usado para reduzir o número de verificações de interseção.
Com os shapefiles que tenho (aproximadamente 4000 polígonos e 4 linhas), o código original executa 12936 .intersection()
verificações e leva cerca de 114 segundos para executar. O segundo pedaço de código que usa o índice espacial executa apenas 1816 .intersection()
verificações, mas também leva aproximadamente 114 segundos para ser executado.
O código para criar o índice espacial leva apenas 1-2 segundos para ser executado. Portanto, as verificações de 1816 no segundo trecho de código estão demorando quase a mesma quantidade de tempo que as 12936 verificam no código original (desde o carregamento do shapefiles e a conversão para geometrias Shapely são iguais em ambas as partes do código).
Não vejo nenhuma razão para que o índice espacial .intersects()
demore mais tempo a verificação; portanto, não sei por que isso está acontecendo.
Só consigo pensar que estou usando o índice espacial RTree incorretamente. Pensamentos?
fonte
intersects()
método leva mais tempo quando o índice espacial está sendo usado (veja a comparação de tempos acima), e é por isso que não tenho certeza se estou usando o índice espacial incorretamente. Ao ler a documentação e os posts vinculados, acho que sou, mas esperava que alguém pudesse apontar se não o fizesse.Apenas para adicionar à resposta mgri.
É importante entender o que é um índice espacial ( como implementar corretamente uma caixa delimitadora para Shapely & Fiona? ). Com o meu exemplo em Como determinar com eficiência quais milhares de polígonos se cruzam com uma cadeia de linhas
Você pode criar um índice espacial com os polígonos
Limites do índice espacial (limites do polígono em verde)
Ou com as LineStrings
Limites do índice espacial (LineString vinculado em vermelho)
Agora, você itera apenas pelas geometrias que têm a chance de se cruzarem com a geometria atual (em amarelo)
Eu uso aqui o índice espacial LineStrings (os resultados são os mesmos, mas com o seu exemplo de 4000 polígonos e 4 linhas ...).
Você também pode usar um gerador ( exemplo.py )
Você pode examinar o script GeoPandas sjoin.py para entender o uso do Rtree .
Existem muitas soluções, mas não esqueça que
fonte
Editar: para esclarecer esta resposta, eu acreditava incorretamente que todos os testes de interseção levavam aproximadamente a mesma quantidade de tempo. Este não é o caso. O motivo pelo qual não obtive a velocidade que eu esperava usando um índice espacial é que a seleção dos testes de interseção é a que demorou mais tempo para fazer.
Como gene e mgri já disseram, um índice espacial não é uma varinha mágica. Embora o índice espacial reduzisse o número de testes de interseção que precisavam ser executados de 12936 a apenas 1816, os testes de 1816 são o teste que levou a maior parte do tempo para calcular em primeiro lugar.
O índice espacial está sendo usado corretamente, mas a suposição de que cada teste de interseção leva aproximadamente a mesma quantidade de tempo é o que está incorreto. O tempo requerido pelo teste de interseção pode variar bastante (0,05 segundos versus 0,000007 segundos).
fonte