Dzielenie wielokątów na * n * liczbę grup równych liczb z ArcPy?


10

Jednym z moich zadań do pracy jest dzielenie paczek na grupy. Grupy te będą wykorzystywane przez agentów do rozmów z właścicielami nieruchomości. Celem jest ułatwienie pracy agenta poprzez grupowanie paczek, które znajdują się blisko siebie, a także podzielenie paczek na równe liczby, aby praca rozkładała się równomiernie. Liczba agentów może wahać się od kilku do 10+.

Obecnie wykonuję to zadanie ręcznie, ale chciałbym zautomatyzować ten proces, jeśli to w ogóle możliwe. Badałem różne narzędzia ArcGIS, ale żadne nie wydaje się odpowiadać mojej potrzebie. Próbowałem skryptu (w pythonie), który wykorzystuje near_analysisi wybiera wielokąty, ale jest raczej losowy i zajmuje wiecznie osiągnięcie półpoprawnego wyniku, który następnie zajmuje mi więcej czasu do naprawy niż gdybym wszystko robił ręcznie od samego początku.

Czy istnieje niezawodna metoda automatyzacji tego zadania?

Przykład wyników (mam nadzieję, że bez podziału widzimy na żółto):

Paczki podzielone


Czy zastanawiałeś się nad analizą alokacji lokalizacji? help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/...
łyko

Czy próbowałeś analizy grupowania (statystyki przestrzenne)?
FelixIP


@crmackey Doceniam link do mojej odpowiedzi, ale nie jestem pewien, jak można ulepszyć połączony kod (dzielenie wielokątów), aby dopasować ten problem (grupowanie wielokątów).
łyk

Odpowiedzi:


4

Oryginalny zestaw:

wprowadź opis zdjęcia tutaj

Utwórz jego pseudo-kopię (przeciągnij CNTRL w TOC) i połącz przestrzennie jeden do wielu za pomocą klonowania. W tym przypadku użyłem odległości 500m. Tabela wyników:

wprowadź opis zdjęcia tutaj

  1. Usuń rekordy z tej tabeli, gdzie PAR_ID = PAR_ID_1 - łatwe.

  2. Iteruj przez tabelę i usuwaj rekordy, gdzie (PAR_ID, PAR_ID_1) = (PAR_ID_1, PAR_ID) dowolnego rekordu powyżej. Nie tak łatwo, użyj acrpy.

Oblicz centroidy zlewni (UniqID = PAR_ID). Są węzłami lub siecią. Połącz je liniami za pomocą przestrzennej tabeli łączenia. To jest osobny temat z pewnością omawiany gdzieś na tym forum.

wprowadź opis zdjęcia tutaj

Poniższy skrypt zakłada, że ​​tabela węzłów wygląda następująco: wprowadź opis zdjęcia tutaj

gdzie MUID pochodzi z paczek, P2013 jest polem do podsumowania. W tym przypadku = 1 tylko do zliczania. [rcvnode] - wyjście skryptu do przechowywania identyfikatora grupy równego NODEREC pierwszego węzła w zdefiniowanej grupie / klastrze.

Łączy strukturę tabeli z wyróżnionymi ważnymi polami

wprowadź opis zdjęcia tutaj

Times przechowuje wagę łącza / krawędzi, tj. Koszt podróży od węzła do węzła. W tym przypadku równa się 1, dzięki czemu koszt podróży do wszystkich sąsiadów jest taki sam. [fi] i [ti] to kolejna liczba połączonych węzłów. Aby zapełnić tę tabelę, przeszukaj to forum na temat przypisywania zi do węzłów do połączenia.

Skrypt dostosowany do mojego własnego środowiska roboczego MXD. Musi zostać zmodyfikowany, na stałe wpisując nazwy pól i źródeł:

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)

ZNAJDŹ WARSTWĘ WĘZŁÓW

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

POBIERZ WARSTWĘ

    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()

Przykład wyjścia dla 6 grup:

wprowadź opis zdjęcia tutaj

Będziesz potrzebował pakietu witryny NETWORKX http://networkx.github.io/documentation/development/install.html

Skrypt przyjmuje wymaganą liczbę klastrów jako parametr (6 w powyższym przykładzie). Wykorzystuje tabele węzłów i łączy, aby utworzyć wykres z jednakową wagą / odległością krawędzi przesuwu (Times = 1). Rozważa połączenie wszystkich węzłów o 2 i oblicza sumę [P2013] w dwóch grupach sąsiadów. Gdy osiągnięty jest wymagany stosunek, np. (6-1) / 1 przy pierwszej iteracji, kontynuuje się ze zmniejszonym stosunkiem docelowym, tj. 4 itd. Do 1. Punkty początkowe mają ogromne znaczenie, więc upewnij się, że twoje „końcowe” węzły siedzą na górze tabeli węzłów (sortowanie?) Zobacz pierwsze 3 grupy w przykładowym wyniku. Pomaga uniknąć „ścinania gałęzi” przy każdej kolejnej iteracji.

Dostosowanie skryptu do pracy z MXD:

  1. nie musisz importować WSPÓLNIE. To moja własna rzecz, która odczytuje moją własną tabelę środowiska, w której określono węzełNodesLayer, theLinksLayer, linksFromI, linksToI. Zamień odpowiednie linie na własne nazwy węzłów i warstw łączy.
  2. Pamiętaj, że pole P2013 może przechowywać wszystko, np. Liczbę najemców lub powierzchnię działki. Jeśli tak, możesz grupować wielokąty, aby pomieścić w przybliżeniu taką samą liczbę osób itp.

W rzeczywistości warstwy węzłów i łączy to tylko elementy wizualne. Oczyszczona tabela łączenia przestrzennego może łatwo zastąpić tabelę łączy, ponieważ od i do węzłów są już przypisane. Tabela wielokątów, może z łatwością służyć jako tabela węzłów, wystarczy dodać pole ReceivingNode i przenieść z niego kolejne numery do „łączy” [FromI] i [ToI].
FelixIP

To wygląda dobrze. Dziękuję bardzo za odpowiedź. Czy możesz wyjaśnić więcej powodów, a nie tylko jak? Komentarze do twojego kodu byłyby ogromne.
Emil Brundage

Śledź hiperłącze w moim wcześniejszym komentarzu do twojego pytania. Próbowałem wyjaśnić to podejście, jeśli to właśnie oznacza „dlaczego”. Cofam swój komentarz dotyczący znaczenia początkowego węzła, ponieważ po opublikowaniu odpowiedzi na twoje pytanie losowo zmieniłem kolejność rekordów próbując zabić skrypt. Nic się nie wydarzyło, nadal dawało rozsądne wyniki.
FelixIP

Aby wyczyścić tabelę połączeń przestrzennych wystarczy usunąć PAR_ID = PAR_ID_1, ponieważ krawędź / link [0,2] na nieukierowanym wykresie NETWORKX równej krawędzi [2,0]. Mogę opublikować zaktualizowany skrypt, nie jestem pewien, czy wpłynie to na moją reputację
FelixIP

@EmilBrundage rzuć okiem, może pomóc z pytaniem gis.stackexchange.com/questions/165057/…
FelixIP

2

Należy użyć narzędzia „Analiza grupowa”, aby osiągnąć swój cel. To narzędzie jest doskonałym narzędziem z przybornika „statystyki przestrzenne”, jak wskazał @phloem. Należy jednak dostroić narzędzie, aby dostosować je do danych i problemu. Stworzyłem podobny scenariusz, jak ten, który opublikowałeś i uzyskałem odpowiedź zbliżoną do twojego celu.

Wskazówka: Korzystając z ArcGIS 10.2, kiedy uruchomiłem to narzędzie, narzekałem na brakujący pakiet pythona „sześć”. Upewnij się więc, że masz zainstalowany pierwszy link

Kroki:

  1. Dodaj pole do swojej klasy wielokątów, aby zachować unikalną wartość
  2. Dodaj kolejne pole typu Short o nazwie np. „SameGroup”
  3. kalkulator pola, aby przypisać 1 do tego pola dla wszystkich wierszy. wystarczy zmienić jeden wiersz na 2. Dodano pole

  4. Ustaw parametry narzędzia „Analiza grupy” w następujący sposób: Analiza grupowa

spróbuj zmienić parametr „Liczba sąsiadów” zgodnie z potrzebami.

Migawki wyników:

Przykładowe wielokąty wejściowe

Wynik analizy grupy


2
Przeszukałem wcześniej analizę grup. Zajmuje się przestrzenią, ale nie liczę, o ile wiem. Całe moje doświadczenie z czytania dokumentacji, patrzenia na twój przykład i wykonywania własnych testów nie pozwala na grupowanie według równej liczby wielokątów.
Emil Brundage

Dlaczego musisz się równać (oczywiście dla agentów)? Ale jeśli dodamy to ograniczenie, to po co grupować (grupować) dane w oparciu o relacje przestrzenne !?
Farid Cheraghi

1
Ponieważ szef tak mówi. Ponadto zminimalizuj czas podróży.
Emil Brundage

1

zasadniczo potrzebujesz metody klastrowania o jednakowym rozmiarze, aby móc wyszukiwać za pomocą tych słów kluczowych w Internecie. Dla mnie jest dobra odpowiedź na stats.SE z implementacją Pythona w jednej z odpowiedzi. Jeśli znasz arkadę, powinieneś być w stanie używać jej z danymi.

Najpierw musisz obliczyć X i Y środkowych wielokątów, a następnie możesz wprowadzić te współrzędne do skryptu i zaktualizować ich tabelę atrybutów za pomocą kursora .da.


Link, który podajesz, wydaje się być na dobrej drodze, ale w zasadzie w języku, którego nie rozumiem. W przypadku skryptu nie wiem, jakie są dane wejściowe i nie mogę rozszyfrować żadnego z kodowań, aby dokładnie zrozumieć, co się dzieje. Jest bardzo mało wyjaśnień.
Emil Brundage

0

Cześć, miałem wcześniej podobny problem, więc dałem mu trochę, ale nigdy więcej nie zacząłem, ale po prostu nad tysiącem myślałem

KSZTAŁT WEJŚCIA

Wprowadź kształt

myślałem, że możesz stworzyć kabaretkę na kształcie wejściowym

kabaretki wtedy kabaretka z przecięciem twojego kształtu wejściowego

wejście do obszaru

Następnie możesz obliczyć powierzchnię tych działek wewnątrz nowo przetworzonego wielokąta

Na początku skryptu poszukiwany był wielokąt wejściowy / n-ta liczba równych rozmiarów

Będziesz wtedy potrzebować sposobu powiązania paczek, aby byli świadomi tych, które są graniczne.

Następnie możesz przejść przez kursor wiersza sumowania paczek

Zasady są

* Dzieli granicę z ostatniego lata * Nie został zsumowany * Gdy przekroczy wartość obliczoną jako równy obszar, cofnie się i będzie to grupa * Proces rozpocznie się od nowa * Ostatnia grupa może być suma pozostałych paczek

myślę, że nawiązanie relacji między paczkami może być trudną rzeczą, ale kiedy to się stanie, myślę, że można to zautomatyzować


Obawiam się, że nie rozumiem, co to ma wspólnego z moim problemem. Co cięcie wieloboku za pomocą sieci ryb ma wspólnego z grupowaniem wielokątów przestrzennie i według równych liczb? Wyglądasz na skoncentrowanego na obszarze, a nie na liczeniu. Powierzchnia (rozmiar) wielokątów działki nie jest czynnikiem. Niezależnie od tego, jak duża lub mała jest paczka, nadal jest tylko jeden właściciel nieruchomości. Zobacz mój przykład, gdzie czerwony jest obszarem wiejskim i rozprzestrzenia się szeroko, podczas gdy pomarańczowy jest miastem, a zatem obejmuje znacznie mniejszą powierzchnię całkowitą.
Emil Brundage

cześć, przepraszam, totalnie źle przeczytałem twoje pytanie. myślę, że post radouxju może być dobrym pomysłem, ale link idzie mi trochę ponad głowę. Przekształcanie wielokątów w punkty wydaje się logiczne, a następnie grupowanie ich. Może istnieć sposób wprowadzenia układu drogowego, ponieważ odległość od punktu do drogi, a następny punkt może zdefiniować element przestrzenny
Jack Walker


0

To jest moje rozwiązanie dla zdarzeń punktowych. Nie ma gwarancji, że zawsze będzie działać ...

  1. Na warstwie zdarzenia punktowego (warstwa wywołania 1) dodaj kolumny dla x (podwójna), y (podwójna) i uniqueid (długa liczba całkowita)
  2. Otwórz tabelę atrybutów dla warstwy 1. Oblicz punkt współrzędnych x dla x, punkt współrzędnych y dla y i FID dla unikalnego identyfikatora
  3. Uruchom narzędzie Statystyka przestrzenna> Klastry mapowania> Analiza grupowania
    • ustaw warstwę1 jako funkcje wejściowe
    • ustaw unikalny jako Unikatowy identyfikator pola
    • Zdefiniuj liczbę grup (powiemy 10)
    • Wybierz xiy dla pól analizy
    • Wybierz „NO_SPATIAL_CONSTRAINT” jako Ograniczenia przestrzenne
    • Kliknij OK
  4. Wykonaj Narzędzia statystyki przestrzennej> Pomiar rozkładów geograficznych> Średnie centrum
    • Wybierz Wyjście z nr 3 jako klasę cech wejściowych
    • Wybierz SS_Group jako Case Case
    • Kliknij OK
  5. Otwórz Network Analyst> Narzędzie alokacji lokalizacji
    • Załaduj wyjście nr 4 jako wyposażenie
    • Załaduj warstwę1 jako Punkty Żądania
    • Otwórz atrybuty i ustaw
      • Rodzaj problemu jako maksymalizacja pokrycia pojemnościowego
      • Udogodnienia do wyboru jako 10 (od nr 3 powyżej)
      • Domyślna pojemność jako łączna liczba funkcji w warstwie 1 podzielona przez obiekty do wyboru w zaokrągleniu w górę (więc jeśli 145 obiektów i 10 obiektów / obszarów zostanie ustawione jako 15)
      • Kliknij OK
        • Rozwiązać
        • Punkty zapotrzebowania powinny być mniej więcej jednakowo podzielone na 10 klastrów geograficznych

Utknąłem w kroku piątym twojej metody. Sprawdziłem rozszerzenie Network Analyst i dodałem pasek narzędzi Network Analyst. Ale większość z nich jest wyszarzona i nie widzę „Narzędzia alokacji lokalizacji”. Używam 10.1.
Emil Brundage

0

Najpierw musisz utworzyć sieciowy zestaw danych, używając swoich ulic. Wypróbowałem tę zaproponowaną metodę i do tej pory miałem więcej szczęścia, robiąc to samo z Grupowaniem (krok 3), używając współrzędnych X, Y i K-średnich dla pól wejściowych (nie idealne, ale szybsze i bliższe temu, czym jestem potrzebujący). Jestem otwarty na komentarze i opinie innych osób.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.