Trovare in modo efficiente i vicini del 1 ° ordine di poligoni 200k


14

Per ognuno dei 208.781 gruppi di blocchi censimento, vorrei recuperare gli ID FIPS di tutti i vicini del primo ordine. Ho scaricato e unito tutti i confini di TIGER in un singolo shapefile da 1 GB.

Ho provato uno script ArcPython che utilizza SelectLayerByLocation per BOUNDARY_TOUCHES al suo interno, ma impiega più di 1 secondo per ogni gruppo di blocchi che è più lento di quanto vorrei. Questo è anche dopo che ho limitato la ricerca SelectLayerByLocation per bloccare i gruppi nello stesso stato. Ho trovato questo script , ma utilizza anche SelectLayerByLocation internamente, quindi non è più veloce.

La soluzione non deve essere basata su Arc: sono aperto ad altri pacchetti, anche se mi sento più a mio agio nel programmare con Python.


2
Dalla versione 9.3, ci sono stati strumenti nella casella degli strumenti Statistiche spaziali per farlo. A partire da 10.0, sono molto efficienti. Ricordo di aver eseguito un'operazione simile su uno shapefile di dimensioni comparabili (tutti i blocchi all'interno di uno stato) e si è completato in 30 minuti, di cui 15 solo per l'I / O del disco - e due anni fa su una macchina molto più lenta. Anche il codice sorgente di Python è accessibile.
whuber

Quale strumento di geoprocessing in Statistiche spaziali hai usato?
Dmahr,

1
Dimentico il suo nome; è specificamente per la creazione di una tabella di relazioni di vicini poligonali. Il sistema di guida ti incoraggia a creare questa tabella prima di eseguire qualsiasi strumento di statistica spaziale basato sul vicino, in modo che gli strumenti non debbano ricalcolare al volo queste informazioni ogni volta che eseguono. Una limitazione significativa, almeno nella versione 9.x, era che l'output era in formato .dbf. Per un file di forma di input di grandi dimensioni che non funzionerà, nel qual caso è necessario suddividere l'operazione in pezzi o hackerare il codice Python per l'output in un formato migliore.
whuber


Sì è quello. Il codice Python sfrutta appieno le capacità interne di ArcGIS (che utilizzano indici spaziali), rendendo l'algoritmo abbastanza veloce.
whuber

Risposte:


3

Se hai accesso ad ArcGIS 10.2 per Desktop, o possibilmente in precedenza, penso allo strumento Polygon Neighbors (Analisi) che:

Crea una tabella con statistiche basate sulla contiguità dei poligoni (sovrapposizioni, bordi coincidenti o nodi).

Vicini poligonali

potrebbe rendere questo compito molto più semplice ora.


Grazie PolyGeo. Ho aggiornato la risposta accettata in modo che lo strumento Poligoni vicini ottenga un po 'più di visibilità. È sicuramente più robusto del mio metodo manuale basato su Python, anche se non sono sicuro di come si possa confrontare la scalabilità con grandi set di dati.
dmahr

Attualmente sto usando 10.3 e non riesce sul mio shapefile con blocchi di censimento di ~ 300K.
soandos,

@soandos Sembra che valga la pena ricercare / porre una nuova domanda.
PolyGeo

8

Per una soluzione che evita ArcGIS, utilizzare pysal . È possibile ottenere i pesi direttamente dai shapefile utilizzando:

w = pysal.rook_from_shapefile("../pysal/examples/columbus.shp")

o

w = pysal.queen_from_shapefile("../pysal/examples/columbus.shp")

Testa per la documentazione per ulteriori informazioni.


3

Solo un aggiornamento. Dopo aver seguito il consiglio di Whuber, ho scoperto che la matrice Generate Spatial Weights utilizza semplicemente loop e dizionari Python per determinare i vicini. Ho riprodotto il processo di seguito.

La prima parte attraversa tutti i vertici di ogni gruppo di blocchi. Crea un dizionario con coordinate di vertice come chiavi e un elenco di ID di gruppi di blocchi che hanno un vertice su quella coordinata come valore. Si noti che ciò richiede un set di dati topologicamente accurato, poiché solo la perfetta sovrapposizione vertice / vertice verrà registrata come relazione adiacente. Fortunatamente, gli shapefile del gruppo di blocchi TIGER dell'Ufficio censimento sono OK al riguardo.

La seconda parte scorre di nuovo attraverso ogni vertice di ogni gruppo di blocchi. Crea un dizionario con gli ID del gruppo di blocchi come chiavi e gli ID vicini del gruppo di blocchi come valori.

# Create dictionary of vertex coordinate : [...,IDs,...]
BlockGroupVertexDictionary = {}
BlockGroupCursor = arcpy.SearchCursor(BlockGroups.shp)
BlockGroupDescription = arcpy.Describe(BlockGroups.shp)
BlockGroupShapeFieldName = BlockGroupsDescription.ShapeFieldName
#For every block group...
for BlockGroupItem in BlockGroupCursor :
    BlockGroupID = BlockGroupItem.getValue("BKGPIDFP00")
    BlockGroupFeature = BlockGroupItem.getValue(BlockGroupShapeFieldName)
    for BlockGroupPart in BlockGroupFeature:
        #For every vertex...
        for BlockGroupPoint in BlockGroupPart:
            #If it exists (and isnt empty interior hole signifier)...
            if BlockGroupPoint:
                #Create string version of coordinate
                PointText = str(BlockGroupPoint.X)+str(BlockGroupPoint.Y)
                #If coordinate is already in dictionary, append this BG's ID
                if PointText in BlockGroupVertexDictionary:
                    BlockGroupVertexDictionary[PointText].append(BlockGroupID)
                #If coordinate is not already in dictionary, create new list with this BG's ID
                else:
                    BlockGroupVertexDictionary[PointText] = [BlockGroupID]
del BlockGroupItem
del BlockGroupCursor


#Create dictionary of ID : [...,neighbors,...]
BlockGroupNeighborDictionary = {}
BlockGroupCursor = arcpy.SearchCursor(BlockGroups.shp)
BlockGroupDescription = arcpy.Describe(BlockGroups.shp)
BlockGroupShapeFieldName = BlockGroupDescription.ShapeFieldName
#For every block group
for BlockGroupItem in BlockGroupCursor:
    ListOfBlockGroupNeighbors = []
    BlockGroupID = BlockGroupItem.getValue("BKGPIDFP00")
    BlockGroupFeature = BlockGroupItem.getValue(BlockGroupShapeFieldName)
    for BlockGroupPart in BlockGroupFeature:
        #For every vertex
        for BlockGroupPoint in BlockGroupPart:
            #If it exists (and isnt interior hole signifier)...
            if BlockGroupPoint:
                #Create string version of coordinate
                PointText = str(BlockGroupPoint.X)+str(BlockGroupPoint.Y)
                if PointText in BlockGroupVertexDictionary:
                    #Get list of block groups that have this point as a vertex
                    NeighborIDList = BlockGroupVertexDictionary[PointText]
                    for NeighborID in NeighborIDList:
                        #Don't add if this BG already in list of neighbors
                        if NeighborID in ListOfBGNeighbors:
                            pass
                        #Add to list of neighbors (as long as its not itself)
                        elif NeighborID != BlockGroupID:
                            ListOfBGNeighbors.append(NeighborID)
    #Store list of neighbors in blockgroup object in dictionary
    BlockGroupNeighborDictionary[BlockGroupID] = ListOfBGNeighbors

del BlockGroupItem
del BlockGroupCursor
del BlockGroupVertexDictionary

Con il senno di poi mi rendo conto che avrei potuto usare un metodo diverso per la seconda parte che non richiedeva di nuovo il loop nel file di forma. Ma questo è quello che ho usato e funziona abbastanza bene anche per migliaia di gruppi di blocchi alla volta. Non ho provato a farlo con tutti gli Stati Uniti, ma può essere eseguito per un intero stato.


2

Un'alternativa potrebbe essere l'uso di PostgreSQL e PostGIS . Ho fatto alcune domande su come eseguire calcoli simili su questo sito:

Ho scoperto che esiste una curva di apprendimento ripida per capire come i vari pezzi del software si incastrano, ma l'ho trovato meraviglioso per fare calcoli su grandi livelli vettoriali. Ho eseguito alcuni calcoli del vicino più vicino su milioni di poligoni ed è stato veloce rispetto ad ArcGIS.


1

Solo alcuni commenti ... il metodo esri / ArcGIS attualmente utilizza i dizionari per contenere le informazioni, ma i calcoli di base vengono eseguiti in C ++ utilizzando lo strumento Poligoni vicini. Questo strumento genera una tabella che contiene le informazioni di contiguità e attrs opzionali come la lunghezza del confine condiviso. È possibile utilizzare lo strumento Matrice generati pesi spaziali se si desidera archiviare e riutilizzare successivamente le informazioni più e più volte. Puoi anche usare questa funzione in WeightsUtilities per generare un dizionario [accesso casuale] con le informazioni sulla contiguità:

contDict = polygonNeighborDict(inputFC, masterField, contiguityType = "ROOK")

dove inputFC = qualsiasi tipo di classe caratteristica poligonale, masterField è il campo "ID univoco" di numeri interi e contiguità in {"ROOK", "QUEEN"}.

Ci sono sforzi in esri per saltare l'aspetto tabulare per gli utenti di Python e passare direttamente a un iteratore che renderebbe molti casi d'uso molto più veloci. PySAL e il pacchetto spdep in R sono alternative fantastiche [vedi la risposta di radek] . Penso che ti venga richiesto di usare gli shapefile come formato di dati in questi pacchetti che sono in sintonia con questo formato di input dei thread. Non sono sicuro di come gestiscono i poligoni sovrapposti e i poligoni all'interno dei poligoni. Generare SWM così come la funzione che ho descritto conterà quei rapporti spaziali come vicini "ROOK" E "QUEEN".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.