Dividere i poligoni in * n * numero di gruppi di uguale numero con ArcPy?


10

Uno dei miei compiti per il lavoro è quello di dividere i pacchi in gruppi. Questi gruppi verranno utilizzati dagli agenti per parlare con i proprietari. L'obiettivo è semplificare il lavoro dell'agente raggruppando i pacchi vicini l'uno all'altro, nonché dividere i pacchi in numeri uguali in modo da distribuire uniformemente il lavoro. Il numero di agenti può variare da una coppia a 10+.

Attualmente eseguo questa operazione manualmente, ma vorrei automatizzare il processo, se possibile. Ho esplorato vari strumenti ArcGIS, ma nessuno sembra soddisfare le mie esigenze. Ho provato uno script (in Python) che utilizza near_analysise seleziona i poligoni, ma è piuttosto casuale e impiega un'eternità a ottenere un risultato semi-corretto che poi impiega più tempo a correggere che se avessi fatto tutto manualmente dall'inizio.

Esiste un metodo affidabile per automatizzare questa attività?

Esempio di risultati (si spera senza la divisione che vediamo in giallo):

Pacchi divisi


Hai esaminato l'analisi dell'allocazione della posizione? help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/...
floema

Hai provato l'analisi di raggruppamento (statistiche spaziali)?
FelixIP,

Ho anche pubblicato uno pseudo-codice della procedura effettiva che sto usando, vedere se potrebbe aiutare gis.stackexchange.com/questions/123289/…
FelixIP,

@crmackey Apprezzo il collegamento alla mia risposta, ma non sono sicuro di come sia possibile modificare il codice collegato (divisione dei poligoni) per adattarlo a questo problema (raggruppamento dei poligoni).
floema il

Risposte:


4

Set originale:

inserisci qui la descrizione dell'immagine

Crea una pseudo-copia (trascinamento CNTRL in TOC) e crea l'unione spaziale da una a molte con il clone. In questo caso ho usato la distanza 500m. Tabella di output:

inserisci qui la descrizione dell'immagine

  1. Rimuovi i record da questa tabella in cui PAR_ID = PAR_ID_1 - facile.

  2. Scorrere la tabella e rimuovere i record in cui (PAR_ID, PAR_ID_1) = (PAR_ID_1, PAR_ID) di qualsiasi record sopra di essa. Non così facile, usa acrpy.

Calcola i centroidi del bacino (UniqID = PAR_ID). Sono nodi o rete. Collegali per linee usando la tabella dei join spaziali. Questo argomento separato è sicuramente trattato da qualche parte in questo forum.

inserisci qui la descrizione dell'immagine

Lo script seguente presuppone che la tabella dei nodi sia simile a quella: inserisci qui la descrizione dell'immagine

dove MUID proveniva da pacchi, P2013 è il campo da riassumere. In questo caso = 1 solo per il conteggio. [rcvnode] - output dello script per memorizzare l'ID del gruppo uguale a NODEREC del primo nodo nel gruppo / cluster definito.

Collega la struttura della tabella con i campi importanti evidenziati

inserisci qui la descrizione dell'immagine

Il negozio memorizza i tempi / peso del bordo, ovvero il costo del viaggio da un nodo all'altro. Uguale a 1 in questo caso in modo che il costo del viaggio per tutti i vicini sia lo stesso. [fi] e [ti] sono il numero sequenziale di nodi collegati. Per popolare questa tabella cerca in questo forum come assegnare da e ai nodi per il collegamento.

Script personalizzato per il mio workbench mxd. Deve essere modificato, codificato con la tua denominazione dei campi e delle fonti:

import arcpy, traceback, os, sys,time
import itertools as itt
scriptsPath=os.path.dirname(os.path.realpath(__file__))
os.chdir(scriptsPath)
import COMMON
sys.path.append(r'C:\Users\felix_pertziger\AppData\Roaming\Python\Python27\site-packages')
import networkx as nx
RATIO = int(arcpy.GetParameterAsText(0))

try:
    def showPyMessage():
        arcpy.AddMessage(str(time.ctime()) + " - " + message)
mxd = arcpy.mapping.MapDocument("CURRENT")
theT=COMMON.getTable(mxd)

TROVA STRATO NODI

theNodesLayer = COMMON.getInfoFromTable(theT,1)
theNodesLayer = COMMON.isLayerExist(mxd,theNodesLayer)

OTTIENI LO STRATO DEI LINK

    theLinksLayer = COMMON.getInfoFromTable(theT,9)
    theLinksLayer = COMMON.isLayerExist(mxd,theLinksLayer)
    arcpy.SelectLayerByAttribute_management(theLinksLayer, "CLEAR_SELECTION")        
    linksFromI=COMMON.getInfoFromTable(theT,14)
    linksToI=COMMON.getInfoFromTable(theT,13)
    G=nx.Graph()
    arcpy.AddMessage("Adding links to graph")
    with arcpy.da.SearchCursor(theLinksLayer, (linksFromI,linksToI,"Times")) as cursor:
            for row in cursor:
                (f,t,c)=row
                G.add_edge(f,t,weight=c)
            del row, cursor
    pops=[]
    pops=arcpy.da.TableToNumPyArray(theNodesLayer,("P2013"))
    length0=nx.all_pairs_shortest_path_length(G)
    nNodes=len(pops)
    aBmNodes=[]
    aBig=xrange(nNodes)
    host=[-1]*nNodes
    while True:
            RATIO+=-1
            if RATIO==0:
                    break
            aBig = filter(lambda x: x not in aBmNodes, aBig)
            p=itt.combinations(aBig, 2)
            pMin=1000000
            small=[]
            for a in p:
                    S0,S1=0,0
                    for i in aBig:
                            p=pops[i][0]
                            p0=length0[a[0]][i]
                            p1=length0[a[1]][i]
                            if p0<p1:
                                    S0+=p
                            else:
                                    S1+=p
                    if S0!=0 and S1!=0:
                            sMin=min(S0,S1)                        
                            sMax=max(S0,S1)
                            df=abs(float(sMax)/sMin-RATIO)
                            if df<pMin:
                                    pMin=df
                                    aBest=a[:]
                                    arcpy.AddMessage('%s %i %i' %(aBest,sMax,sMin))
                            if df<0.005:
                                    break
            lSmall,lBig,S0,S1=[],[],0,0
            arcpy.AddMessage ('Ratio %i' %RATIO)
            for i in aBig:
                    p0=length0[aBest[0]][i]
                    p1=length0[aBest[1]][i]
                    if p0<p1:
                            lSmall.append(i)
                            S0+=p0
                    else:
                            lBig.append(i)
                            S1+=p1
            if S0<S1:
                    aBmNodes=lSmall[:]
                    for i in aBmNodes:
                            host[i]=aBest[0]
                    for i in lBig:
                            host[i]=aBest[1]
            else:
                    aBmNodes=lBig[:]
                    for i in aBmNodes:
                            host[i]=aBest[1]
                    for i in lSmall:
                            host[i]=aBest[0]

    with arcpy.da.UpdateCursor(theNodesLayer, "rcvnode") as cursor:
            i=0
            for row in cursor:
                    row[0]=host[i]
                    cursor.updateRow(row)
                    i+=1

            del row, cursor
except:
    message = "\n*** PYTHON ERRORS *** "; showPyMessage()
    message = "Python Traceback Info: " + traceback.format_tb(sys.exc_info()[2])[0]; showPyMessage()
    message = "Python Error Info: " +  str(sys.exc_type)+ ": " + str(sys.exc_value) + "\n"; showPyMessage()

Esempio di output per 6 gruppi:

inserisci qui la descrizione dell'immagine

È necessario il pacchetto del sito NETWORKX http://networkx.github.io/documentation/development/install.html

Lo script accetta il numero richiesto di cluster come parametro (6 nell'esempio precedente). Utilizza tabelle di nodi e collegamenti per creare un grafico con uguale peso / distanza dei bordi della corsa (tempi = 1). Considera la combinazione di tutti i nodi per 2 e calcola il totale di [P2013] in due gruppi di vicini. Quando viene raggiunto il rapporto richiesto, ad es. (6-1) / 1 alla prima iterazione, continua con l'obiettivo di rapporto ridotto, ovvero 4, ecc. Fino a 1. I punti di partenza sono di enorme importanza, quindi assicurarsi che i nodi 'end' siano posizionati in alto della tabella dei nodi (ordinamento?) Vedi i primi 3 gruppi nell'output di esempio. Aiuta a evitare il "taglio dei rami" ad ogni iterazione successiva.

Personalizzazione degli script per funzionare da mxd:

  1. non è necessario importare COMUNE. È la mia cosa, che legge la mia tabella di ambiente, dove specificato theNodesLayer, theLinksLayer, linksFromI, linksToI. Sostituisci le linee pertinenti con la tua denominazione di nodi e livelli di collegamenti.
  2. Si noti che il campo P2013 può memorizzare qualsiasi cosa, ad esempio il numero di inquilini o l'area dei pacchi. In tal caso, potresti raggruppare poligoni per contenere approssimativamente lo stesso numero di persone, ecc.

In realtà i livelli di nodi e collegamenti sono solo cose visive. La tabella ripulita del join spaziale può facilmente sostituire la tabella dei collegamenti, poiché i nodi da e verso sono già assegnati. La tabella Polygons, può facilmente servire come tabella dei nodi, basta aggiungere il campo ReceivingNode e trasferire i numeri sequenziali da esso a 'links' [FromI] e [ToI].
FelixIP,

Questo sembra buono. Grazie mille per la risposta. Puoi spiegare di più sul perché e non solo sul come? I commenti sul tuo codice sarebbero enormi.
Emil Brundage,

Segui il collegamento ipertestuale nel mio commento precedente alla tua domanda. Ho cercato di spiegare l'approccio, se questo è ciò che significa "perché". Ritiro il mio commento sull'importanza di iniziare il nodo, perché dopo aver pubblicato la risposta alla tua Q, ho cambiato casualmente l'ordine dei record cercando di uccidere lo script. Non è successo nulla, ha comunque prodotto risultati ragionevoli.
FelixIP

Per ripulire la tabella dei join spaziali è sufficiente eliminare PAR_ID = PAR_ID_1, perché edge / link [0,2] nel grafico non orientato di NETWORKX uguale edge [2,0]. Posso pubblicare script aggiornati, non sono sicuro che influenzerà la mia reputazione
FelixIP,

@EmilBrundage dai un'occhiata, potrebbe aiutare con il motivo per cui domanda gis.stackexchange.com/questions/165057/…
FelixIP

2

Dovresti utilizzare lo strumento "Analisi di gruppo" per raggiungere il tuo obiettivo. Questo strumento è un ottimo strumento dal toolbox "statistiche spaziali" come ha sottolineato @phloem. Tuttavia, è necessario ottimizzare lo strumento per adattarsi ai dati e al problema. Ho creato uno scenario simile a quello che hai pubblicato e ho ottenuto la risposta vicina al tuo obiettivo.

Suggerimento: usando ArcGIS 10.2, quando ho eseguito lo strumento, si è lamentato del pacchetto mancante di Python, "sei". Quindi assicurati di averlo installato per primo Link

passi:

  1. Aggiungi un campo alla tua classe poligonale per contenere un valore univoco
  2. Aggiungi un altro campo di tipo Short con il nome es. "SameGroup"
  3. calcolatrice sul campo per assegnare 1 a questo campo per tutte le righe. basta cambiare una riga in 2. Campo aggiunto

  4. Impostare i parametri dello strumento "Analisi di gruppo" in questo modo: Analisi di gruppo

prova a cambiare il parametro "Numero di vicini" in base alle tue esigenze.

Istantanee dei risultati:

Poligoni di input di esempio

Risultato dell'analisi di gruppo


2
Ho esaminato prima l'analisi del gruppo. Si occupa di spazio, ma non conta per quanto ne so. Tutta la mia esperienza nella lettura della documentazione, nell'esame del tuo esempio e nell'esecuzione dei miei test non mi consente di raggruppare per uguale numero di poligoni.
Emil Brundage,

Perché è necessario rendere uguali (fuori rotta per gli agenti)? Ma se aggiungiamo quel vincolo, allora perché raggruppare (raggruppare) i dati in base alla relazione spaziale !?
Farid Cheraghi,

1
Perché lo dice il capo. Inoltre, ridurre al minimo il tempo di viaggio.
Emil Brundage,

1

fondamentalmente vuoi un metodo di clustering di uguali dimensioni, quindi puoi cercare con queste parole chiave sul web. Per me, c'è una buona risposta su stats.SE con un'implementazione di Python in una delle risposte. Se hai familiarità con arcpy dovresti essere in grado di usarlo con i tuoi dati.

Devi prima calcolare la X e Y dei centroidi dei tuoi poligoni, quindi puoi inserire queste coordinate nello script e aggiornare la loro tabella degli attributi usando un cursore .da.


Il link che fornisci sembra essere sulla strada giusta, ma è fondamentalmente in una lingua che non capisco. Per lo script non so quali siano gli input e non riesco a decifrare nessuno dei codici per capire esattamente cosa sta succedendo. C'è pochissima spiegazione.
Emil Brundage,

0

Ciao, ho avuto un problema simile a questo prima, quindi ne avevo dato un po ', ma non ne ho mai iniziato un altro, ma solo sul lato thoery stavo pensando

FORMA DI INGRESSO

Forma di input

stavo pensando che potresti creare una rete da pesca sulla forma di input

a rete la rete da pesca con un'intersezione della forma di input sarebbe quindi

ingresso in area

È quindi possibile calcolare l'area di questi pacchi all'interno del poligono appena elaborato

All'inizio del tuo script l'area di input poligono / ennesima quantità di uguali dimensioni desiderate

Avresti quindi bisogno di un modo per mettere in relazione i pacchi in modo che siano consapevoli di quelli che sono delimitati.

Quindi potresti passare attraverso un cursore di riga che riassume i pacchi

Essere regole

* Condivide un confine con l'ultima estate * Non è stato sommato * Una volta superato il valore calcolato come area uguale, farebbe un passo indietro e questo sarebbe un gruppo * Il processo ricomincerebbe da capo * L'ultimo gruppo potrebbe essere la somma dei pacchi rimasti

penso che stabilire la relazione tra i pacchi potrebbe essere la cosa difficile, ma una volta fatto penso che potrebbe essere possibile automatizzarlo


Temo di non capire cosa abbia a che fare con il mio problema. Cosa ha a che fare tagliare un poligono con una rete da pesca con il raggruppamento di poligoni spazialmente e in numero uguale? Sembri concentrato sull'area, non conta. L'area (dimensioni) dei poligoni dei pacchi non è un fattore. Indipendentemente da quanto sia grande o piccolo un pacco, è ancora solo un proprietario di proprietà con cui parlare. Vedi il mio esempio in cui il rosso è una zona rurale e si diffonde ampiamente, mentre l'arancione è urbano e copre quindi un'area totale molto più piccola.
Emil Brundage,

ciao, scusa, ho letto male la tua domanda. penso che il post di radouxju potrebbe essere la strada da percorrere, ma il link va un po 'sopra la mia testa. Trasformare i poligoni in punti sembra logico e quindi raggrupparli. Potrebbe esserci un modo per introdurre il sistema stradale come la distanza dal punto alla strada e il punto successivo potrebbe definire l'elemento spaziale
Jack Walker,


0

Questa è la mia soluzione per eventi a punti. Nessuna garanzia funzionerà sempre ...

  1. Sul tuo livello evento punto (chiama layer1) aggiungi colonne per x (doppio), y (doppio) e uniqueid (intero lungo)
  2. Apri la tabella degli attributi per il livello 1. Calcola il punto di coordinata x per x, il punto di coordinata y per y e il FID per l'id univoco
  3. Esegui lo strumento Statistiche spaziali> Mappatura dei cluster> Raggruppamento analisi
    • imposta layer1 come funzionalità di input
    • imposta uniqueid come ID campo univoco
    • Definisci il numero di gruppi (diremo 10)
    • Selezionare xey per i campi di analisi
    • Scegli "NO_SPATIAL_CONSTRAINT" per Vincoli spaziali
    • Clicca OK
  4. Esegui Strumenti statistiche spaziali> Misurazione delle distribuzioni geografiche> Centro medio
    • Selezionare Output da # 3 come Classe caratteristiche di input
    • Seleziona SS_Group come campo caso
    • Clicca OK
  5. Apri Analista di rete> Strumento di allocazione della posizione
    • Carica l'output del numero 4 come strutture
    • Carica layer1 come punti di domanda
    • Apri attributi e imposta
      • Tipo di problema come Massimizza copertura capacitiva
      • Strutture da scegliere come 10 (dal n. 3 sopra)
      • Capacità predefinita come numero totale di funzionalità nel layer1 diviso per strutture da scegliere arrotondate per eccesso (quindi se 145 funzionalità e 10 strutture / aree, impostare come 15)
      • Clicca OK
        • Risolvere
        • I tuoi punti di domanda dovrebbero essere più o meno equamente distribuiti in 10 cluster geografici

Sono bloccato al passaggio cinque del tuo metodo. Ho verificato l'estensione Network Analyst e ho aggiunto la barra degli strumenti Network Analyst. Ma la maggior parte è oscurata e non vedo "Strumento di allocazione della posizione". Sto usando 10.1.
Emil Brundage,

0

Dovrai prima creare un set di dati di rete usando le tue strade. Ho provato questo metodo proposto e finora ho avuto più fortuna a fare la stessa cosa con il raggruppamento (passaggio 3) da solo, usando le coordinate X, Y e i mezzi k per i campi di input (non perfetto, ma più veloce e più vicino a quello che sono bisogno). Sono aperto ad altri commenti e feedback.

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.