¿Dividiendo polígonos en * n * número de grupos de conteos iguales con ArcPy?

10

Una de mis tareas para el trabajo es dividir las parcelas en grupos. Los agentes utilizarán estos grupos para hablar con los propietarios. El objetivo es facilitar el trabajo del agente mediante la agrupación de parcelas cercanas entre sí, así como dividir las parcelas en números iguales para que el trabajo se distribuya de manera uniforme. El número de agentes puede fluctuar de una pareja a más de 10.

Actualmente realizo esta tarea manualmente, pero me gustaría automatizar el proceso si es posible. He explorado varias herramientas de ArcGIS, pero ninguna parece satisfacer mis necesidades. Probé un script (en Python) que utiliza near_analysisy selecciona polígonos, pero es bastante aleatorio y lleva una eternidad lograr un resultado semicorrecto que luego me lleva más tiempo solucionarlo que si hubiera hecho todo manualmente desde el principio.

¿Existe un método confiable para automatizar esta tarea?

Ejemplo de resultados (con suerte sin la división que vemos en amarillo):

Paquetes divididos

Emil Brundage
fuente
¿Has mirado en el análisis de ubicación y asignación? help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/…
floema
¿Has probado el análisis de agrupamiento (estadísticas espaciales)?
FelixIP
También publiqué un pseudocódigo del procedimiento real que estoy usando, vea si podría ayudar gis.stackexchange.com/questions/123289/…
FelixIP
@crmackey Agradezco el enlace a mi respuesta, pero no estoy seguro de cómo podría modificar el código vinculado (división de polígonos) para que se ajuste a este problema (agrupación de polígonos).
floema

Respuestas:

4

Conjunto original:

ingrese la descripción de la imagen aquí

Cree una pseudo-copia (CNTRL-arrastre en TOC) de ella y haga una unión espacial de uno a muchos con clon. En este caso utilicé la distancia 500m. Tabla de salida:

ingrese la descripción de la imagen aquí

  1. Eliminar registros de esta tabla donde PAR_ID = PAR_ID_1 - fácil.

  2. Itere a través de la tabla y elimine los registros donde (PAR_ID, PAR_ID_1) = (PAR_ID_1, PAR_ID) de cualquier registro que se encuentre encima. No es tan fácil, usa acrpy.

Calcule los centroides de captación (UniqID = PAR_ID). Son nodos o red. Conéctelos por líneas usando la tabla de unión espacial. Este es un tema separado seguramente cubierto en algún lugar de este foro.

ingrese la descripción de la imagen aquí

El siguiente script asume que la tabla de nodos se ve así: ingrese la descripción de la imagen aquí

donde MUID vino de las parcelas, P2013 es un campo para resumir. En este caso = 1 solo para contar. [rcvnode]: salida del script para almacenar la ID de grupo igual a NODEREC del primer nodo en el grupo / clúster definido.

Estructura de la tabla de enlaces con campos importantes resaltados

ingrese la descripción de la imagen aquí

Times almacena el peso del enlace / borde, es decir, el costo de viaje de un nodo a otro. Igual 1 en este caso para que el costo de viaje a todos los vecinos sea el mismo. [fi] y [ti] son ​​un número secuencial de nodos conectados. Para completar esta tabla, busque en este foro cómo asignar desde y hacia nodos para vincular.

Script personalizado para mi propio workbench mxd. Tiene que ser modificado, codificado con su nombre de los campos y fuentes:

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)

ENCONTRAR CAPA DE NODOS

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

OBTENER CAPA DE ENLACES

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

Ejemplo de salida para 6 grupos:

ingrese la descripción de la imagen aquí

Necesitará el paquete de sitio NETWORKX http://networkx.github.io/documentation/development/install.html

El script toma el número requerido de clústeres como parámetro (6 en el ejemplo anterior). Utiliza tablas de nodos y enlaces para hacer un gráfico con igual peso / distancia de los bordes de viaje (Times = 1). Considera la combinación de todos los nodos por 2 y calcula el total de [P2013] en dos grupos de vecinos. Cuando se alcanza la proporción requerida, por ejemplo (6-1) / 1 en la primera iteración, continúa con el objetivo de proporción reducida, es decir, 4, etc. hasta 1. Los puntos de inicio son de gran importancia, así que asegúrese de que sus nodos 'finales' se encuentren en la parte superior de su tabla de nodos (¿ordenando?) Vea los primeros 3 grupos en la salida de ejemplo. Ayuda a evitar el 'corte de ramas' en cada próxima iteración.

Personalización de script para trabajar desde mxd:

  1. no necesitas importar COMÚN. Es lo mío, que lee mi propia tabla de entorno, donde se especifica theNodesLayer, theLinksLayer, linksFromI, linksToI. Reemplace las líneas relevantes con su propio nombre de nodos y capas de enlaces.
  2. Tenga en cuenta que el campo P2013 puede almacenar cualquier cosa, por ejemplo, número de inquilinos o área de parcela. Si es así, puede agrupar polígonos para contener aproximadamente el mismo número de personas, etc.
FelixIP
fuente
En realidad, los nodos y las capas de enlaces son solo cosas visuales. La tabla de unión espacial limpiada puede reemplazar fácilmente la tabla de enlaces, ya que desde y hacia los nodos ya están asignados. La tabla de polígonos puede servir fácilmente como tabla de nodos, simplemente agregue el campo ReceivingNode y transfiera los números secuenciales de nuevo a 'enlaces' [FromI] y [ToI].
FelixIP
Esto luce bien. Muchas gracias por la respuesta. ¿Puedes explicar más por qué y no solo cómo? Los comentarios sobre su código serían enormes.
Emil Brundage
Siga el hipervínculo en mi comentario anterior a su pregunta. He tratado de explicar el enfoque, si esto es lo que significa "por qué". Retiro mi comentario sobre la importancia del nodo de inicio, porque después de publicar la respuesta a su Q, cambié aleatoriamente el orden de los registros tratando de matar el script. No pasó nada, todavía produjo resultados razonables.
FelixIP
Para limpiar la tabla de unión espacial es suficiente eliminar PAR_ID = PAR_ID_1, porque edge / link [0,2] en el gráfico no dirigido de NETWORKX igual edge [2,0]. Puedo publicar un script actualizado, no estoy seguro si afectará mi reputación
FelixIP
@EmilBrundage eche un vistazo, podría ayudar con por qué pregunta gis.stackexchange.com/questions/165057/…
FelixIP
2

Debe utilizar la herramienta "Análisis de grupo" para lograr su objetivo. Esta herramienta es una gran herramienta de la caja de herramientas "estadísticas espaciales" como señaló @phloem. Sin embargo, debe ajustar la herramienta para adaptarla a sus datos y problemas. Creé un escenario similar al que publicaste y obtuve la respuesta cercana a tu objetivo.

Sugerencia: Al usar ArcGIS 10.2, cuando ejecuté la herramienta, se quejó del paquete de python que faltaba, "seis". Así que asegúrese de tenerlo instalado primero Enlace

Pasos:

  1. Agregue un campo a su clase de polígono para mantener un valor único
  2. Agregue otro campo de tipo Short con el nombre, por ejemplo, "SameGroup"
  3. su calculadora de campo para asignar 1 a este campo para todas las filas. solo cambia una fila a 2. Campo agregado

  4. Establezca los parámetros de la herramienta "Análisis de grupo" de esta manera: Análisis grupal

intente cambiar el parámetro "Número de vecinos" para adaptarlo a sus necesidades.

Instantáneas de resultados:

Muestra de polígonos de entrada

Resultado del análisis grupal

Farid Cheraghi
fuente
2
Miré en el análisis de grupo antes. Se trata de espacial, pero no cuenta hasta donde puedo decir. Toda mi experiencia de leer documentación, mirar su ejemplo y realizar mis propias pruebas no permite agrupar por igual número de polígonos.
Emil Brundage
¿Por qué necesita hacer igual (fuera de curso para los agentes)? Pero si agregamos esa restricción, ¿por qué agrupar (agrupar) los datos según la relación espacial?
Farid Cheraghi
1
Porque el jefe lo dice. Además, minimice el tiempo de viaje.
Emil Brundage
1

básicamente desea un método de agrupación de igual tamaño, para poder buscar con estas palabras clave en la web. Para mí, hay una buena respuesta en estadísticas. SE con una implementación de Python en una de las respuestas. Si está familiarizado con arcpy, debería poder usarlo con sus datos.

Primero debe calcular la X e Y de los centroides de sus polígonos, luego puede ingresar estas coordenadas en el script y actualizar su tabla de atributos con un cursor .da.

radouxju
fuente
El enlace que proporciona parece estar en el camino correcto, pero básicamente está en un idioma que no entiendo. Para el script, no sé cuáles son las entradas y no puedo descifrar ninguna de las codificaciones para comprender exactamente lo que está sucediendo. Hay muy poca explicación.
Emil Brundage
0

Hola, tuve un problema similar a este antes, así que le di un poco, sin embargo, nunca comencé con otro, pero solo en el lado que estaba pensando

FORMA DE ENTRADA

Forma de entrada

Estaba pensando que podría crear una red de pesca en la forma de entrada

mallas mallas con una intersección de su forma de entrada entonces

entrada en el área

Luego puede calcular el área de estas parcelas dentro del polígono recién procesado

Al comienzo de su script, el área ingresó la cantidad de polígono / enésima cantidad de tamaños iguales deseados

Luego, necesitaría una forma de relacionar las parcelas para que sepan cuáles están bordeadas.

Entonces podrías pasar por un cursor de fila para resumir las parcelas

Reglas siendo

* Comparte un borde con el último verano * No se ha sumado * Una vez que supera el valor calculado como el área igual, retrocedería y esto sería un grupo * El proceso comenzaría de nuevo * El último grupo podría ser la suma de las parcelas sobrantes

Creo que establecer la relación entre las parcelas puede ser complicado, pero una vez hecho esto, creo que podría ser posible automatizarlo.

Jack Walker
fuente
Me temo que no entiendo qué tiene que ver esto con mi problema. ¿Qué tiene que ver cortar un polígono con una red de pesca con agrupar polígonos espacialmente y en números iguales? Parece centrado en el área, no cuenta. El área (tamaño) de los polígonos de parcela no es un factor. Independientemente de cuán grande o pequeño sea un paquete, todavía es solo un propietario con quien hablar. Vea mi ejemplo donde el rojo es un área rural y se extiende ampliamente, mientras que el naranja es urbano y cubre un área total mucho más pequeña.
Emil Brundage
hola a ti, perdón, he leído mal tu pregunta. Creo que la publicación de Radouxju podría ser el camino a seguir, pero el enlace pasa un poco por mi cabeza. Convertir los polígonos en puntos parece lógico y luego agruparlos. Puede haber una forma de introducir el sistema de carreteras como la distancia desde el punto a la carretera y el siguiente punto podría definir el elemento espacial
Jack Walker
0

Esta es mi solución para eventos puntuales. No hay garantías de que siempre funcionará ...

  1. En su capa de evento de punto (llamada capa1) agregue columnas para x (doble), y (doble) y uniqueid (entero largo)
  2. Abra la tabla de atributos para la capa 1. Calcule el punto de coordenadas x para x, el punto de coordenadas y para y, y el FID para una identificación única
  3. Herramienta Ejecutar estadísticas espaciales> Asignación de clústeres> Análisis de agrupamiento
    • establecer layer1 como características de entrada
    • establecer uniqueid como ID de campo único
    • Definir número de grupos (diremos 10)
    • Seleccione x e y para los campos de análisis.
    • Elija "NO_SPATIAL_CONSTRAINT" para Restricciones espaciales
    • Haga clic en Aceptar
  4. Ejecutar herramientas de estadísticas espaciales> Medición de distribuciones geográficas> Centro medio
    • Seleccione la salida del n. ° 3 como clase de características de entrada
    • Seleccione SS_Group como campo de caso
    • Haga clic en Aceptar
  5. Open Network Analyst> Herramienta de asignación de ubicación
    • Salida de carga del n. ° 4 como instalaciones
    • Cargar layer1 como puntos de demanda
    • Abrir atributos y establecer
      • Tipo de problema como maximizar la cobertura capacitada
      • Instalaciones para elegir como 10 (del # 3 arriba)
      • Capacidad predeterminada como el número total de entidades en la capa 1 dividido por las instalaciones para elegir redondeadas (por lo tanto, si 145 entidades y 10 instalaciones / áreas, se establecen como 15)
      • Haga clic en Aceptar
        • Resolver
        • Sus puntos de demanda deben distribuirse más o menos equitativamente en 10 grupos geográficos.
LilHeb
fuente
Estoy atrapado en el paso cinco de tu método. Revisé la extensión de Network Analyst y agregué la barra de herramientas de Network Analyst. Pero la mayor parte está atenuada y no veo "Herramienta de asignación de ubicación". Estoy usando 10.1.
Emil Brundage
0

Necesitará crear un conjunto de datos de red primero usando sus calles. He probado este método propuesto y hasta ahora he tenido más suerte haciendo lo mismo con Agrupación (paso 3) por sí mismo, usando coordenadas X, Y y k-medias para los campos de entrada (no perfecto, pero más rápido y más cercano a lo que soy) necesitando). Estoy abierto a otros comentarios y opiniones.

Chris
fuente