Первая линия: насколько магазин стоит в проходном месте?
ГлебКоротко: код по расчёту betweenness centrality здесь, по индивидуальному расчёту взвешенных скоров здесь, под расчёт "как надо" пока виртуалку не выбил, поэтому беру замену из классов дорог ОСМ; карта, на которой можно посмотреть результаты трёх методов расчёта - здесь; скажите мне, какой из методов вам нравится больше и как бы вы его изменили / дополнили
Задача бьётся на 2 этапа:
- определить "центральность" графа улично-дорожной сети (дальше УДС)
- присудить точкам ПВЗ степень центральности в зависимости от того, как далеко она находится от рёбер графа + есть ли что-то (дом, "неважная" дорога) на её пути и т.д.
Этап 1 - определение центральности графа УДС
пробовал 2 подхода:
А. Расчёт betweenness centrality (в методике space syntax это называется choice) - это когда мы в грАфе строим кратчайшие пути изо всех рёбер во все и ранжируем "центральность" в зависимости от количества кратчайших путей, пролегающих через то или иное ребро - так в любом городе всегда подсвечиваются ключевые магистрали - они-то нам и нужны!
Б. Извлечение меры центральности из свойств графа в открытых данных OSM: в значительной части мира дороги из графа OSM размечены по классам - их можно использовать как прокси меры центральности
1А. Расчёт betweenness centrality
технические параметры (не геозадротам - пропустить до следующего пункта):
код лежит здесь, инструкция по запуску - здесь
пробовал выгружать весь граф через geofabrik и через osmnx (graph_from_polygon). Проблема и там и там - граф слишком большой, поэтому:
в первом случае я делил на сетку h3 6 разрешения (опытным путём выяснил что свойства графа не теряются именно на этом разрешении), делал буфер в x3 ребра гекса (10км) от центроида, клипал граф по этому буферу, и считал его edge_betweenness_centrality - таким образом мы считаем "локальную" центральность, но при этом даём графу контекст за пределами гекса (после подсчёта складываю рассчитанное только внутри гекса - не весь буфер)
во втором случае брал буферизованые на 10 км административные полигоны, polyfill-ил их гексами 6 разрешения, для каждого гекса от центроида также давал буфер и индивидуально выгружал через ox.graph_from_polygon. Да, есть оверхэд - у нас пересекаются выгружаемые части графов, но сразу загружать граф на, скажем, весь Казахстан было очень долго (если вообще возможно), а писать логику укрупнения гексов, чтобы загружать граф и потом их назад дробить - я и так уже перемудрил, решил тут не париться
считал через momepy.betweenness_centrality. Считал именно через edge (можно ещё через узлы - весь расчёт в momepy делается через networkx + есть прикрученная возможность для узлов считать по локальному радиусу, но когда попробовал усреднить значения узлов на рёбра - получилась каша:
сэмплировать можно; на network_type='all' разницы между полным расчётом и сэмплом к=1000 разницы почти не увидел:
В итоге параметры такие: считаем edge_betweenness_centrality, network_type='all', k=1500
1Б. Извлечение меры центральности из OSM
здесь тупо делаю словарик, где каждому виду дорог экспертно присваиваю балл (чем выше - тем важнее дорога; детальное описание здесь):
Этап 2 - от графа к точкам
С графом определились - супер; как нам от этой абстракции прийти к простому ответу - магазин в проходном месте или нет?
-- А в чём, собственно, проблема, шеф?
а проблемы могут быть вот какие:
- стоит рядом с проходным местом, но фасад обращён от дороги во двор
- стоит нормально, но точка ПВЗ проставлена неверно
- то, что будет считаться второстепенной дорогой в крупном городе - будет главной в небольшом поселении
- сравнивать центральность одного пвз с центральностью других пвз поблизости может быть опасно - что если они все хреново расположены, а наш чуть менее хреново чем остальные? метрика должна быть в этом смысле абсолютной
Попробовал 3 метода:
А. тупо дать точке меру центральности ближайшего ребра
Б. нарезать вокруг точки 3 кольца, посчитать средний балл важности от длины всех рёбер и с затуханием (чем дальше дорога - тем меньше весит) посчитать взвешенное
В. найти в радиусе Х от пвз самую важную дорогу, построить до неё кратчайшую линию, посмотреть на 2 вещи: что мы пересекаем (дороги, здания) + насколько эта линия оказалась длинной
2А. Центральность ближайшего ребра
Короче, вердикт - первый вариант совсем не подходит
2Б. Нарезные кольца
Вывод - ок, но можно лучше
2В. Линия к главной дороге поблизости
I. В радиусе 300м нашли дороги с ближайшим скором
II. Построили до них одно кратчайшее ребро из ПВЗ
III. Посмотрели, как пересекается со зданиями и дорогами
IV. Присвоили финальный скор: без пересечений 1м = 1 балл, пересекает здание 1м = 3 балла, пересечение одного ребра дороги = 5 баллов
Раньше считал всё в equidistant projection (epsg:4087) - она сохраняет дистанции, но искажает направления - в итоге стал для каждого ПВЗ приводить к локальному UTM (epsg:326xx) - из-за этого кратчайшая линия ищется более точно
смотрим на результат и пытаемся поймать косяки:
Какие тут потенциальные могут быть проблемы?
за пределом 300 метров у нас полная амнезия: если мы представим себе огромный квартал 600х600 только с "маленькими" дорогами (пешеходные, вело, автобусные), и наш пвз расположен в центре этого квартала - мы оценим этот ПВЗ как идеальный, потому что главной крупной дорогой будет скор 3, и мы будем очень близко к нему расположены
Выводы
Пока остановился на третьем - по взвешенной линии - но мне нужны ваши комментарии и предложения
Как смотреть и критиковать?
Идёшь на карту, включаешь разные слои и сравниваешь: где хорошо, а где плохо
Куда это идёт?
пойдёт в тарифы - есть два пути:
- считать заранее (в гексах) и использовать как коэф для тарифа - ОЧЕНЬ дорого, не хотелось бы
- считать постфактум - не должно быть супер-долго, но каждая точка может занимать секунду-две