Dividindo polígonos em * n * número de grupos de contagens iguais com o ArcPy?


10

Uma das minhas tarefas para o trabalho é dividir as parcelas em grupos. Esses grupos serão usados ​​pelos agentes para conversar com os proprietários. O objetivo é facilitar o trabalho do agente agrupando as parcelas próximas umas das outras e dividir as parcelas em números iguais para que o trabalho seja distribuído uniformemente. O número de agentes pode variar de um par para 10 ou mais.

Atualmente, eu executo essa tarefa manualmente, mas gostaria de automatizar o processo, se possível. Eu explorei várias ferramentas do ArcGIS, mas nenhuma parece atender à minha necessidade. Eu tentei um script (em python) que faz uso near_analysise seleção de polígonos, mas é bastante aleatório e leva uma eternidade para obter um resultado semi-correto que leva mais tempo para ser corrigido do que se eu tivesse feito tudo manualmente desde o início.

Existe um método confiável para automatizar esta tarefa?

Exemplo de resultados (espero que sem a divisão que vemos em amarelo):

Pacotes divididos


Você analisou a análise de alocação de local? help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/…
phloem

Você já tentou a Análise de agrupamento (Estatísticas espaciais)?
FelixIP

Eu também postou um pseudo-código de procedimento real que estou usando, ver se ele pode ajudá- gis.stackexchange.com/questions/123289/...
FelixIP

@crmackey Agradeço o link da minha resposta, mas não sei como você pode ajustar o código vinculado (polígonos de divisão) para se ajustar a esse problema (agrupar polígonos).
Phloem

Respostas:


4

Conjunto original:

insira a descrição da imagem aqui

Crie uma pseudo-cópia (arraste CNTRL no sumário) e faça a junção espacial de uma para muitas com o clone. Neste caso, usei a distância 500m. Tabela de saída:

insira a descrição da imagem aqui

  1. Remova os registros desta tabela onde PAR_ID = PAR_ID_1 - fácil.

  2. Itere a tabela e remova os registros em que (PAR_ID, PAR_ID_1) = (PAR_ID_1, PAR_ID) de qualquer registro acima dela. Não é tão fácil, use acrpy.

Calcular centróides de captação (UniqID = PAR_ID). Eles são nós ou rede. Conecte-os por linhas usando a tabela de junção espacial. Este é um tópico separado, certamente coberto em algum lugar deste fórum.

insira a descrição da imagem aqui

O script abaixo assume que a tabela de nós se parece com isso: insira a descrição da imagem aqui

de onde o MUID veio das parcelas, o P2013 é um campo para resumir. Neste caso = 1 apenas para contagem. [rcvnode] - saída de script para armazenar o ID do grupo igual a NODEREC do primeiro nó no grupo / cluster definido.

Liga a estrutura da tabela com os campos importantes destacados

insira a descrição da imagem aqui

O Times armazena o peso do link / borda, ou seja, o custo da viagem de um nó para outro. Igual a 1 neste caso, para que o custo da viagem para todos os vizinhos seja o mesmo. [fi] e [ti] são um número seqüencial de nós conectados. Para preencher esta tabela, pesquise neste fórum sobre como atribuir de e para nós ao link.

Script personalizado para meu próprio ambiente de trabalho mxd. Tem que ser modificado, codificado permanentemente com a nomeação dos campos e fontes:

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)

ENCONTRE A CAMADA DE NODES

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

GET LINKS LAYER

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

Exemplo de saída para 6 grupos:

insira a descrição da imagem aqui

Você precisará do pacote do site NETWORKX http://networkx.github.io/documentation/development/install.html

O script usa o número necessário de clusters como parâmetro (6 no exemplo acima). Ele está usando tabelas de nós e links para criar um gráfico com peso / distância iguais das arestas de deslocamento (Times = 1). Ele considera a combinação de todos os nós por 2 e calcula o total de [P2013] em dois grupos de vizinhos. Quando a taxa necessária alcançada, por exemplo, (6-1) / 1 na primeira iteração, continua com a meta de taxa reduzida, ou seja, 4, etc. até 1. Os pontos de partida são de grande importância, portanto, verifique se os nós 'finais' estão no topo da tabela de nós (classificando?) Veja os 3 primeiros grupos na saída de exemplo. Isso ajuda a evitar o 'corte de ramificação' a ​​cada próxima iteração.

Customização de script para trabalhar no mxd:

  1. você não precisa importar COMUM. É minha coisa, que lê minha própria tabela de ambiente, onde theNodesLayer, theLinksLayer, linksFromI, linksToI especificado. Substitua as linhas relevantes por sua própria nomeação de nós e camadas de links.
  2. Observe que o campo P2013 pode armazenar qualquer coisa, por exemplo, número de inquilinos ou área de encomendas. Nesse caso, você pode agrupar polígonos para conter aproximadamente o mesmo número de pessoas, etc.

Na realidade, as camadas de nós e links são apenas coisas visuais. A tabela limpa de junção espacial pode substituir facilmente a tabela de links, porque os nós de e para já estão atribuídos. Tabela de polígonos, pode servir facilmente como tabela de nós, basta adicionar o campo ReceivingNode e transferir números sequenciais dele de volta para 'links' [FromI] e [ToI].
FelixIP

Este parece ser bom. Muito obrigado pela resposta. Você pode explicar mais sobre o porquê, e não apenas o como? Os comentários no seu código seriam enormes.
Emil Brundage

Siga o hiperlink no meu comentário anterior à sua pergunta. Eu tentei explicar a abordagem, se é isso que significa "por que". Retiro meu comentário sobre a importância do nó inicial, porque depois de postar a resposta no seu Q, alterei aleatoriamente a ordem dos registros tentando matar o script. Nada aconteceu, ainda produziu resultados razoáveis.
FelixIP

Para limpar a tabela de junção espacial, basta excluir PAR_ID = PAR_ID_1, porque a borda / link [0,2] no gráfico não direcionado da borda igual ao NETWORKX [2,0]. Eu posso postar script atualizado, não tenho certeza se isso vai afetar a minha reputação
FelixIP

@EmilBrundage ter um olhar, pode ajudar com o porquê pergunta gis.stackexchange.com/questions/165057/...
FelixIP

2

Você deve usar a ferramenta "Análise de grupo" para atingir seu objetivo. Esta ferramenta é uma ótima ferramenta da caixa de ferramentas "estatísticas espaciais", como o @phloem apontou. No entanto, você deve ajustar a ferramenta para se adaptar aos seus dados e problemas. Criei um cenário semelhante ao postado e obtive a resposta próxima ao seu objetivo.

Dica: Usando o ArcGIS 10.2, quando executei a ferramenta, ela se queixou do pacote python ausente, "six". Portanto, certifique-se de tê-lo instalado pela primeira vez Fazer a ligação

Passos:

  1. Adicione um campo à sua classe de polígono para manter um valor exclusivo
  2. Adicione outro campo do tipo Short com o nome, por exemplo, "SameGroup"
  3. você calcula a calculadora para atribuir 1 a este campo para todas as linhas. basta alterar uma linha para 2. Campo adicionado

  4. Defina os parâmetros da ferramenta "Análise de grupo" como este: Análise de grupo

tente alterar o parâmetro "Número de vizinhos" para atender às suas necessidades.

Instantâneos do resultado:

Amostra de polígonos de entrada

Resultado da análise de grupo


2
Eu examinei a Análise de grupo antes. Ele lida com espacial, mas não conta tanto quanto eu posso dizer. Toda a minha experiência de ler a documentação, ver o seu exemplo e executar meus próprios testes não permite agrupar por números iguais de polígonos.
Emil Brundage

Por que você precisa fazer igual (fora do curso para os agentes)? Mas se adicionarmos essa restrição, por que agrupar (agrupar) os dados com base na relação espacial !?
Farid Cheraghi

1
Porque o chefe diz isso. Além disso, minimize o tempo de viagem.
Emil Brundage

1

basicamente, você deseja um método de agrupamento de tamanho igual para poder pesquisar com essas palavras-chave na web. Para mim, há uma boa resposta no stats.SE com uma implementação Python em uma das respostas. Se você conhece o arcpy, deve poder usá-lo com seus dados.

Você primeiro precisa calcular o X e o Y dos centróides dos polígonos, depois pode inserir essas coordenadas no script e atualizar a tabela de atributos usando um cursor .da.


O link fornecido por você parece estar no caminho certo, mas é basicamente em um idioma que eu não entendo. Para o script, não sei quais são as entradas e não consigo decifrar nenhuma das codificações para entender exatamente o que está acontecendo. Há muito pouca explicação.
Emil Brundage

0

Olá, eu tive um problema semelhante a este antes, então eu já dei um pouco, mas nunca comecei outro, mas apenas no lado thoery eu estava pensando

FORMA DE ENTRADA

Forma de entrada

eu estava pensando que você poderia criar uma rede de pesca na forma de entrada

rede de pesca arrastão com uma interseção de você forma de entrada seria então

entrada na área

Você pode calcular a área dessas parcelas dentro do polígono recém-processado

No início do seu script, a área insere o polígono / enésima quantidade de tamanhos iguais

Você precisaria, então, de uma maneira de relacionar as parcelas para que elas fiquem cientes daquelas que fazem fronteira.

Então você pode passar por um cursor de linha para resumir as parcelas

Regras sendo

* Ele compartilha uma fronteira com o último verão * Não foi somado * Uma vez que ultrapassa o valor calculado como a área igual, ele recuaria e seria um grupo * O processo começaria novamente * o último grupo poderia ser a soma das parcelas restantes

Eu acho que estabelecer o relacionamento entre as parcelas pode ser a coisa mais complicada, mas uma vez feito isso, acho que será possível automatizá-lo


Receio não entender o que isso tem a ver com o meu problema. O que o corte de um polígono com uma rede de peixes tem a ver com o agrupamento de polígonos espacialmente e em números iguais? Você parece focado na área, não conta. A área (tamanho) dos polígonos das parcelas não é um fator. Independentemente de quão grande ou pequena é uma parcela, ainda é apenas um proprietário para conversar. Veja meu exemplo, onde o vermelho é uma área rural e se espalha amplamente, enquanto o laranja é urbano e, portanto, cobre uma área total muito menor.
Emil Brundage

oi ai você, desculpe, eu total mis ler sua pergunta. acho que o post do radouxju poderia ser o caminho a percorrer, mas o link passa um pouco da minha cabeça. Transformar os polígonos em pontos parece lógico e depois agrupá-los. Pode haver uma maneira de introduzir o sistema rodoviário como a distância entre o ponto e a estrada e o próximo ponto pode definir o elemento espacial
Jack Walker


0

Esta é a minha solução para eventos pontuais. Não há garantias de que sempre funcionará ...

  1. Na camada de eventos do ponto (chamada layer1), adicione colunas para x (double), y (double) e uniqueid (long inteiro)
  2. Abrir tabela de atributos para a camada 1. Calcule x ponto de coordenada para x, y ponto de coordenada para y e FID para id exclusivo
  3. Ferramenta Executar estatística espacial> Clusters de mapeamento> Análise de agrupamento
    • definir layer1 como recursos de entrada
    • definir uniqueid como ID de campo exclusivo
    • Defina o número de grupos (digamos 10)
    • Selecione x e y para os campos de análise
    • Escolha "NO_SPATIAL_CONSTRAINT" para restrições espaciais
    • Clique OK
  4. Executar ferramentas de estatística espacial> Medir distribuições geográficas> Mean Center
    • Selecione Saída de # 3 como Classe de recursos de entrada
    • Selecione SS_Group como campo de caso
    • Clique OK
  5. Open Network Analyst> Ferramenta de alocação de local
    • Carregar saída # 4 como instalações
    • Carregar layer1 como pontos de demanda
    • Abra Atributos e defina
      • Tipo de problema como maximizar a cobertura capacitada
      • Instalações para escolher como 10 (de # 3 acima)
      • Capacidade padrão como o número total de recursos na camada1 dividido por instalações para escolha arredondada (por isso, se 145 recursos e 10 instalações / áreas estiverem definidos como 15)
      • Clique OK
        • Resolver
        • Seus pontos de demanda devem ser distribuídos mais ou menos igualmente em 10 grupos geográficos

Estou preso na etapa cinco do seu método. Verifiquei a extensão do Network Analyst e adicionei a barra de ferramentas do Network Analyst. Mas a maioria está acinzentada e não vejo "Ferramenta de alocação de local". Estou usando 10.1.
Emil Brundage

0

Você precisará criar um conjunto de dados de rede primeiro usando suas ruas. Eu tentei esse método proposto e, até agora, tive mais sorte em fazer o mesmo com o Agrupamento (etapa 3), usando as coordenadas X, Y e meios k para campos de entrada (não é perfeito, mas é mais rápido e mais próximo do que eu sou). precisando). Estou aberto a outros comentários e feedback.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.