Usando OGR e Shapely com mais eficiência? [fechadas]

29

Estou procurando algumas sugestões sobre como tornar meu código python mais eficiente. Normalmente, a eficiência não importa para mim, mas agora estou trabalhando com um arquivo de texto de locais nos EUA com mais de 1,5 milhão de pontos. Com a configuração fornecida, leva cerca de 5 segundos para executar as operações em um ponto; Eu preciso entender esse número.

Estou usando três pacotes GIS python diferentes para fazer algumas operações diferentes nos pontos e gerar um novo arquivo de texto delimitado.

  1. Eu uso o OGR para ler um shapefile de limite do condado e obter acesso à geometria do limite.
  2. Shapely verifica se há um ponto em qualquer um desses municípios.
  3. Se estiver dentro de um, eu uso a Biblioteca Shapefile do Python para extrair informações de atributo do limite .dbf.
  4. Depois, escrevo algumas informações de ambas as fontes em um arquivo de texto.

Suspeito que a ineficiência esteja em ter um loop de 2 a 3 camadas ... não sei bem o que fazer sobre isso. Estou particularmente procurando ajuda com alguém experiente no uso de qualquer um desses 3 pacotes, pois é a primeira vez que utilizo um deles.

import os, csv
from shapely.geometry import Point
from shapely.geometry import Polygon
from shapely.wkb import loads
from osgeo import ogr
import shapefile

pointFile = "C:\\NSF_Stuff\\NLTK_Scripts\\Gazetteer_New\\NationalFile_20110404.txt"
shapeFolder = "C:\NSF_Stuff\NLTK_Scripts\Gazetteer_New"
#historicBounds = "C:\\NSF_Stuff\\NLTK_Scripts\\Gazetteer_New\\US_Counties_1860s_NAD"
historicBounds = "US_Counties_1860s_NAD"
writeFile = "C:\\NSF_Stuff\\NLTK_Scripts\\Gazetteer_New\\NewNational_Gazet.txt"

#opens the point file, reads it as a delimited file, skips the first line
openPoints = open(pointFile, "r")
reader = csv.reader(openPoints, delimiter="|")
reader.next()

#opens the write file
openWriteFile = open(writeFile, "w")

#uses Python Shapefile Library to read attributes from .dbf
sf = shapefile.Reader("C:\\NSF_Stuff\\NLTK_Scripts\\Gazetteer_New\\US_Counties_1860s_NAD.dbf")
records = sf.records()
print "Starting loop..."

#This will loop through the points in pointFile    
for row in reader:
    print row
    shpIndex = 0
    pointX = row[10]
    pointY = row[9]
    thePoint = Point(float(pointX), float(pointY))
    #This section uses OGR to read the geometry of the shapefile
    openShape = ogr.Open((str(historicBounds) + ".shp"))
    layers = openShape.GetLayerByName(historicBounds)
    #This section loops through the geometries, determines if the point is in a polygon
    for element in layers:
        geom = loads(element.GetGeometryRef().ExportToWkb())
        if geom.geom_type == "Polygon":
            if thePoint.within(geom) == True:
                print "!!!!!!!!!!!!! Found a Point Within Historic !!!!!!!!!!!!"
                print str(row[1]) + ", " + str(row[2]) + ", " + str(row[5]) + " County, " + str(row[3])
                print records[shpIndex]
                openWriteFile.write((str(row[0]) + "|" + str(row[1]) + "|" + str(row[2]) + "|" + str(row[5]) + "|" + str(row[3]) + "|" + str(row[9]) + "|" + str(row[10]) + "|" + str(records[shpIndex][3]) + "|" + str(records[shpIndex][9]) + "|\n"))
        if geom.geom_type == "MultiPolygon":
            for pol in geom:
                if thePoint.within(pol) == True:
                    print "!!!!!!!!!!!!!!!!! Found a Point Within MultiPolygon !!!!!!!!!!!!!!"
                    print str(row[1]) + ", " + str(row[2]) + ", " + str(row[5]) + " County, " + str(row[3])
                    print records[shpIndex]
                    openWriteFile.write((str(row[0]) + "|" + str(row[1]) + "|" + str(row[2]) + "|" + str(row[5]) + "|" + str(row[3]) + "|" + str(row[9]) + "|" + str(row[10]) + "|" + str(records[shpIndex][3]) + "|" + str(records[shpIndex][9]) + "|\n"))
        shpIndex = shpIndex + 1
    print "finished checking point"
    openShape = None
    layers = None


pointFile.close()
writeFile.close()
print "Done"
GrantD
fonte
3
Você pode considerar este destacamento @ Code Review: codereview.stackexchange.com
RyanDalton

Respostas:

21

O primeiro passo seria mover o shapefile aberto para fora do loop de linhas, você está abrindo e fechando o shapefile 1,5 milhão de vezes.

Para ser sincero, eu colocaria tudo no PostGIS e o faria usando SQL em tabelas indexadas.

Ian Turton
fonte
19

Uma rápida olhada no seu código traz algumas otimizações à mente:

  • Verifique cada ponto contra a caixa delimitadora / envelope dos polígonos primeiro, para eliminar discrepâncias óbvias. Você poderia dar um passo adiante e contar o número de bboxes em que um ponto se encontra, se for exatamente um, não precisará ser testado com a geometria mais complexa (bem, na verdade será se estiver em mais mais de um, ele precisará ser testado mais adiante. Você pode fazer duas passagens para eliminar os casos simples dos casos complexos).

  • Em vez de percorrer cada ponto e testá-lo contra polígonos, faça um loop entre os polígonos e teste cada ponto. O carregamento / conversão da geometria é lento, então você deseja fazer o mínimo possível. Além disso, crie uma lista de pontos a partir do CSV inicialmente, novamente para evitar ter que fazer isso várias vezes por ponto e depois descartar os resultados no final dessa iteração.

  • Indexe espacialmente seus pontos, o que envolve a conversão para um arquivo shapefile, SpatialLite ou algo como um banco de dados PostGIS / PostgreSQL . Isso tem a vantagem de que ferramentas como o OGR poderão fazer a maior parte do trabalho para você.

  • Não escreva a saída até o final: print () é uma função cara na melhor das hipóteses. Em vez disso, armazene os dados como uma lista e grave-os no final, usando as funções padrão de decapagem do Python ou funções de despejo de lista.

MerseyViking
fonte
5
Os dois primeiros vão render muito. Você também pode acelerar um pouco as coisas usando ogr para tudo, em vez de Shapely e Shapefile.
Sgillies
2
Para qualquer coisa relacionada a "Python" e "índice espacial", não procure além de Rtree , pois é muito rápido em encontrar formas próximas a outras formas
Mike T