Деление полигонов на * n * количество групп равного количества с ArcPy?

10

Одна из моих задач для работы - разделить посылки на группы. Эти группы будут использоваться агентами для общения с владельцами недвижимости. Цель состоит в том, чтобы упростить работу агента, сгруппировав участки, которые находятся рядом друг с другом, а также разделить участки на равные числа, чтобы работа распределялась равномерно. Количество агентов может колебаться от пары до 10+.

В настоящее время я выполняю эту задачу вручную, но хотел бы автоматизировать процесс, если это вообще возможно. Я исследовал различные инструменты ArcGIS, но ни один из них не подходит мне. Я попробовал скрипт (на python), который использует near_analysisи выбирает многоугольники, но он довольно случайный и требует вечного времени для получения полукорректного результата, который потом занимает больше времени, чем если бы я делал все вручную с самого начала.

Есть ли надежный способ автоматизировать эту задачу?

Пример результатов (надеюсь, без деления мы видим желтым цветом):

Разделенные посылки

Эмиль Брундейдж
источник
Вы смотрели в анализ распределения местоположения? help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/…
флоэма
Вы пробовали групповой анализ (пространственная статистика)?
FelixIP
Я также разместил псевдокод фактической процедуры, которую я использую, посмотрите, может ли это помочь gis.stackexchange.com/questions/123289/…
FelixIP
@crmackey Я ценю ссылку на мой ответ, но я не уверен, как можно настроить связанный код (расщепление многоугольников) для решения этой проблемы (группирование многоугольников).
флоэма

Ответы:

4

Оригинальный набор:

введите описание изображения здесь

Создайте его псевдокопию (перетаскиванием CNTRL в оглавлении) и сделайте пространственное соединение один ко многим с клоном. В этом случае я использовал расстояние 500м. Выходная таблица:

введите описание изображения здесь

  1. Удалить записи из этой таблицы, где PAR_ID = PAR_ID_1 - легко.

  2. Выполните итерацию по таблице и удалите записи, где (PAR_ID, PAR_ID_1) = (PAR_ID_1, PAR_ID) любой записи над ней. Не так просто, используйте acrpy.

Вычислить центроиды водосбора (UniqID = PAR_ID). Это узлы или сеть. Соедините их линиями, используя таблицу пространственного соединения. Это отдельная тема, наверняка освещенная где-то на этом форуме.

введите описание изображения здесь

В приведенном ниже сценарии предполагается, что таблица узлов выглядит следующим образом: введите описание изображения здесь

где MUID получен из посылок, P2013 - это поле для подведения итогов. В этом случае = 1 только для подсчета. [rcvnode] - вывод сценария для сохранения идентификатора группы, равного NODEREC первого узла в определенной группе / кластере.

Связывает структуру таблицы с выделенными важными полями

введите описание изображения здесь

Times хранит вес ссылки / края, то есть стоимость перемещения от узла к узлу. В этом случае равен 1, так что стоимость проезда всех соседей одинакова. [fi] и [ti] - последовательный номер подключенных узлов. Чтобы заполнить эту таблицу, поищите в этом форуме информацию о том, как назначать узлы и ссылки на них.

Скрипт настроен под мой рабочий стол mxd. Должен быть изменен, жестко запрограммирован с указанием имен полей и источников:

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)

НАЙТИ СЛОЙ СЛОЯ

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

ПОЛУЧИТЬ ССЫЛКИ СЛОЯ

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

Пример вывода для 6 групп:

введите описание изображения здесь

Вам понадобится пакет сайта NETWORKX http://networkx.github.io/documentation/development/install.html

Скрипт принимает необходимое количество кластеров в качестве параметра (6 в примере выше). Он использует таблицы узлов и ссылок для построения графа с равным весом / расстоянием ребер перемещения (Times = 1). Он учитывает объединение всех узлов на 2 и вычисляет общее количество [P2013] в двух группах соседей. Когда требуемое соотношение достигнуто, например, (6-1) / 1 на первой итерации, продолжается с уменьшенным целевым отношением, т.е. 4 и т. Д. До 1. Начальные точки имеют огромное значение, поэтому убедитесь, что ваши «конечные» узлы расположены сверху вашей таблицы узлов (сортировка?) Смотрите первые 3 группы в примере вывода. Это помогает избежать «обрезки веток» на каждой следующей итерации.

Настройка скрипта для работы с mxd:

  1. вам не нужно импортировать COMMON. Это моя собственная вещь, которая читает мою собственную таблицу окружения, в которой указаны NodesLayer, theLinksLayer, linksFromI, linksToI. Замените соответствующие строки собственным именованием узлов и слоев ссылок.
  2. Обратите внимание, что в поле P2013 может храниться что угодно, например количество арендаторов или площадь посылки. Если это так, вы можете кластеризовать полигоны, чтобы держать примерно одинаковое количество людей и т. Д.
FelixIP
источник
В реальности узлы и слои ссылок - это просто визуальные вещи. Очищенная таблица пространственного соединения может легко заменить таблицу ссылок, потому что узлы уже назначены. Таблица полигонов может легко служить таблицей узлов, просто добавьте поле ReceivingNode и перенесите из него последовательные числа обратно в «ссылки» [FromI] и [ToI].
FelixIP
Это выглядит хорошо. Большое спасибо за ответ. Можете ли вы объяснить больше почему, а не только как? Комментарии к вашему коду будут огромными.
Эмиль Брюндейдж
Пожалуйста, перейдите по гиперссылке в моем предыдущем комментарии к вашему вопросу. Я попытался объяснить подход, если это то, что означает «почему». Я забрал свой комментарий относительно важности запуска узла, потому что после публикации ответа на ваш вопрос я случайно изменил порядок записей, пытаясь уничтожить скрипт. Ничего не произошло, все равно дало разумные результаты.
FelixIP
Для очистки таблицы пространственного соединения достаточно удалить PAR_ID = PAR_ID_1, потому что ребро / ссылка [0,2] в неориентированном графе NETWORKX равны ребру [2,0]. Я могу опубликовать обновленный скрипт, не уверен, повлияет ли он на мою репутацию
FelixIP
@EmilBrundage посмотрите, это может помочь с вопросом, почему gis.stackexchange.com/questions/165057/…
FelixIP
2

Вы должны использовать инструмент «Групповой анализ» для достижения своей цели. Этот инструмент является отличным инструментом из набора инструментов «пространственная статистика», как указывал @phloem. Однако вы должны точно настроить инструмент для адаптации к вашим данным и проблемам. Я создал похожий сценарий, подобный тому, который вы опубликовали, и получил ответ, близкий к вашей цели.

Подсказка: используя ArcGIS 10.2, когда я запускал инструмент, он жаловался на отсутствующий пакет python, «шестерка». Поэтому убедитесь , что он установлен первой Ссылка

шаги:

  1. Добавьте в класс полигона поле для хранения уникального значения
  2. Добавьте другое поле типа Short с именем, например "SameGroup"
  3. Вы полевой калькулятор, чтобы назначить 1 этому полю для всех строк. просто измените одну строку на 2. Добавлено поле

  4. Установите параметры инструмента «Групповой анализ» следующим образом: Групповой анализ

Попробуйте изменить параметр «Количество соседей» в соответствии с вашими потребностями.

Снимки результата:

Пример ввода полигонов

Результат группового анализа

Фарид Чераги
источник
2
Я посмотрел в групповом анализе ранее. Он имеет дело с пространством, но не считается, насколько я могу судить. Весь мой опыт чтения документации, просмотра вашего примера и выполнения моих собственных тестов не позволяет группировать по равному количеству полигонов.
Эмиль Брюндейдж
Зачем вам делать равные (вне курса для агентов)? Но если мы добавим это ограничение, то зачем кластеризовать (группировать) данные, основанные на пространственных отношениях !?
Фарид Чераги
1
Потому что так говорит босс. Кроме того, минимизируйте время в пути.
Эмиль Брюндейдж
1

в основном вам нужен метод кластеризации одинакового размера, чтобы вы могли искать по этим ключевым словам в сети. Для меня есть хороший ответ на stats.SE с реализацией Python в одном из ответов. Если вы знакомы с arcpy, вы сможете использовать его со своими данными.

Сначала вам нужно вычислить X и Y центроидов ваших полигонов, затем вы можете ввести эти координаты в сценарий и обновить их таблицу атрибутов, используя курсор .da.

radouxju
источник
Ссылка, которую вы предоставляете, кажется, находится на правильном пути, но в основном на языке, который я не понимаю. Что касается сценария, я не знаю, что такое входные данные, и не могу расшифровать кодировку, чтобы точно понять, что происходит. Там очень мало объяснений.
Эмиль Брюндейдж
0

Привет, у меня была такая же проблема, как и раньше, поэтому я дал ее, хотя, никогда не начинал с другой, это только на стороне thoery, я думал

ВХОДНАЯ ФОРМА

Форма ввода

я думал, что вы можете создать рыболовную сеть на входной форме

рыболовная сеть рыболовная сеть с пересечением вашей входной формы будет затем

вход в область

Затем вы можете рассчитать площадь этих участков внутри вновь обработанного многоугольника.

В начале вашего скрипта требуется область ввода полигона / n-го количества равных размеров

Тогда вам понадобится способ связать посылки, чтобы они знали о тех, которые ограничены.

Тогда вы могли бы пройти курсором строки суммирования посылок

Правила бытия

* Он разделяет границу с последним летом * Он не суммируется * После того, как он превысит значение, рассчитанное как равную площадь, он отступит назад, и это будет группа * Процесс начнется заново * Последняя группа может быть сумма отправленных посылок

я думаю, что установление отношений между участками может быть сложной задачей, но как только это будет сделано, я думаю, что можно было бы автоматизировать это

Джек уокер
источник
Боюсь, я не понимаю, какое это имеет отношение к моей проблеме. Какое отношение имеет разрезание многоугольника с помощью рыболовной сети к пространственному и равному количеству группируемых многоугольников? Вы, кажется, сосредоточены на области, а не в счет. Площадь (размер) полигонов участков не является фактором. Независимо от того, насколько большая или маленькая посылка, это все еще один владелец недвижимости, с которым можно поговорить. Посмотрите на мой пример, где красный цвет - это сельская местность, которая широко распространена, а оранжевый - городская, поэтому она занимает гораздо меньшую общую площадь.
Эмиль Брундэйдж
Привет тебе, извини, я полностью пропустил твой вопрос. я думаю, что сообщение Radouxju могло бы быть способом пойти, но связь идет немного по моей голове. Превращение полигонов в точки кажется логичным, а затем группировка их. Может быть способ представить дорожную систему как расстояние от точки до дороги, а следующая точка может определить пространственный элемент
Джек Уокер,
0

Я считаю, что расширение, которое вы ищете, это район. Это обычно используется для выборов, но так же как для равных территорий привилегии размера. (Размер не обязательно означает для области, это может быть любая демография)

http://www.esri.com/software/arcgis/extensions/districting

http://help.arcgis.com/en/redistricting/pdf/Districting_for_ArcGIS_Help.pdf

Целик А
источник
0

Это мое решение для точечных событий. Никаких гарантий, это всегда будет работать ...

  1. На вашем точечном слое событий (вызов layer1) добавьте столбцы для x (double), y (double) и uniqueid (длинное целое)
  2. Откройте таблицу атрибутов для слоя 1. Вычислите x координатную точку для x, y координатную точку для y и FID для уникального идентификатора
  3. Инструмент «Выполнение пространственной статистики»> «Отображение кластеров»> «Групповой анализ»
    • установить layer1 в качестве входных объектов
    • установить uniqueid как уникальный идентификатор поля
    • Определите количество групп (скажем, 10)
    • Выберите x и y для полей анализа
    • Выберите «NO_SPATIAL_CONSTRAINT» для пространственных ограничений
    • Нажмите ОК
  4. Инструменты выполнения пространственной статистики> Измерение географических распределений> Средний центр
    • Выберите выход из # 3 в качестве класса входных объектов
    • Выберите SS_Group в качестве поля дела
    • Нажмите ОК
  5. Откройте Network Analyst> Средство размещения местоположения
    • Нагрузка выхода № 4 в качестве объектов
    • Загрузить layer1 как точки спроса
    • Откройте атрибуты и установите
      • Тип проблемы как максимизация охвата емкостью
      • Услуги на выбор как 10 (из № 3 выше)
      • Емкость по умолчанию - общее количество объектов в слое 1, разделенное на объекты, которые нужно выбрать округленные (так что если 145 объектов и 10 объектов / областей, установите значение 15)
      • Нажмите ОК
        • Решать
        • Ваши точки спроса должны быть более или менее равномерно распределены по 10 географическим кластерам.
LilHeb
источник
Я застрял на пятом шаге вашего метода. Я проверил расширение Network Analyst и добавил панель инструментов Network Analyst. Но большая часть выделена серым цветом, и я не вижу «Инструмент распределения местоположения». Я использую 10.1.
Эмиль Брундейдж
0

Сначала вам нужно будет создать набор сетевых данных, используя ваши улицы. Я попробовал этот предложенный метод, и до сих пор мне больше повезло, делая то же самое с Группировкой (шаг 3), используя координаты X, Y и k-средних для полей ввода (не идеально, но быстрее и ближе к тому, что я есть). необходимости). Я открыт для других комментариев и отзывов.

Крис
источник