Русский
Русский
English
Статистика
Реклама

Osm

Как найти количество всех букв на всех знаках вида въезд в город Х в стране? Точный способ ответить на такие вопросы

24.08.2020 18:21:28 | Автор: admin
Недавно в рамках одного собеседования мне понадобилось решить задачу, условие которой приведено ниже:
У лучшего в мире управляющего по имени Пенультимо родилась очередная гениальнейшая идея, peализовать которую вам и предстоит. Он верит, что поток туристов на Исла-де-Эдукадос повысится, если он сможет рассказать всему миру, как же много замечательных дорожных знаков с длинными надписями eсть у них на острове. Вам предлагается придумать алгоритм, позволяющий подсчитать суммарное количество букв на всех знаках Въезд в город Х на острове, а затем применить полученные знания для подсчёта аналогичной метрики для Республики Беларусь. Обратите внимание язык, используемый для обозначения населённых пунктов, а также тот факт, что въездов в город может быть несколько. Пенультимо также приветствует инициативность, так что можете исследовать этот вопрос для отдельных областей, провести сравнение с количеством людей, проживающих в области, а также провести любые другие исследования, которые покажутся Вам интересными.

Под катом покажу точное решение этой и других похожих задач, например: Сколько АЗС находится в пределах Москвы?

Общий метод решения


Если взглянуть на карту OpenStreetMap, то на ум сразу приходит следующая идея: а давайте для каждого города получим его границы и дороги, находящиеся внутри его границ, а потом найдем их пересечения, на которых будут стоять знаки! Как будем искать пересечения: берем отрезок границы, потом отрезок дороги и смотрим, пересекаются ли они (типичная геометрическая задача). И так пока не кончатся все отрезки и города.
Про архитектуру данных OSM
Вообще, есть три главных компонента: узлы, линии и отношения
Каждый элемент имеет свой ID, а также свои теги.
  • Узел это просто точка на карте, имеющая кроме ID также широту и долготу
  • Линия это отсортированный список узлов, который представляет контур здания или отдельную улицу
  • Отношение это список, состоящий из узлов, линий или других отношений, представляющий логические или географические связи между объектами на карте


OverPass


OverPass Это API для получения данных из OpenStreetMap. Оно имеет свой язык составления запросов, про него подробно можно почитать В этой статье.
Для того чтобы было проще и удобнее составлять запросы, есть инструмент Overpass-turbo, где результат выполнения запроса можно посмотреть в удобном и интерактивном виде.

Использование OverPass API в Python


Для обработки данных из OSM в Питоне можно использовать пакет Overpy в качестве оболочки.
Для отправки запросов и получения данных нужно проделать следующее:
import overpyapi = overpy.Overpass()Data = api.query("""*ваш запрос*""")

где в переменной(?) Data и содержится все, что выдал нам сервер.
Как обработать эти данные? Предположим, что мы ввели следующий запрос на получение границ Минска:
relation["type"="boundary"]["boundary"="administrative"]["name:be"="Мнск"];//Дословно: отношение вида административная граница города Минска>; out skel qt;

На выходе имеем файл XML (можно выбрать Json), имеющий следующую структуру:
<*шапка файла*><далее идет подобное перечисление всех узлов>  <node id="277218521" lat="53.8605688" lon="27.3946601"/>  <node id="4623647835" lat="53.8603938" lon="27.3966685"/>  <node id="4713906615" lat="53.8605343" lon="27.3998220"/>  <node id="4713906616" lat="53.8605398" lon="27.3966820"/>  <node id="4713906617" lat="53.8605986" lon="27.3947987"/>  <node id="277050633" lat="53.8463790" lon="27.4431241"/>  <node id="277050634" lat="53.8455797" lon="27.4452681"/>  <node id="4713906607" lat="53.8460017" lon="27.4439797"/><далее указаны пути и ID узлов, из которых они состоят><way id="572768148">    <nd ref="5502433452"/>    <nd ref="277218520"/>    <nd ref="4713906620"/>    <nd ref="277218521"/>    <nd ref="4713906617"/>    <nd ref="4623647835"/>    <nd ref="4713906616"/></way><way id="29079842">    <nd ref="277212682"/>    <nd ref="277051005"/>    <nd ref="4739822889"/>    <nd ref="4739822888"/>    <nd ref="4739845423"/>    <nd ref="4739845422"/>    <nd ref="4739845421"/></way>

Давайте получим некоторые данные:
import overpyapi = overpy.Overpass()Data = api.query("""relation["type"="boundary"]["boundary"="administrative"]["name:be"="Мнск"];>; out skel qt;""")Xa=Data.ways[0].nodes[0].lon #получаем долготу первого узла первой линииYa=Data.ways[0].nodes[0].lat #получаем широтуXb=Data.ways[0].nodes[1].lonYb=Data.ways[0].nodes[1].latNodeID=Data.ways[0]._node_ids[0] #получаем ID первого узла первой линииprint(len(Data.nodes)) #получаем общее количество узловprint(NodeID)print(Xa,Ya)print(Xb,Yb)

С точки зрения работы с OpenStreetMap в питоне, это всё, что понадобится, чтобы достать данные.

Перейдем непосредственно к задаче


Для ее решения написан код на Питоне, увидеть его можно под спойлером. Просьба сильно не ругать за качество кода, это первый такой большой проект на нем.
Заголовок спойлера
import overpy###########################def line_intersection(line1, line2): #функция поиска пересечений    ax1 = line1[0][0]    ay1 = line1[0][1]    ax2 = line1[1][0]    ay2 = line1[1][1]    bx1 = line2[0][0]    by1 = line2[0][1]    bx2 = line2[1][0]    by2 = line2[1][1]    v1 = (bx2 - bx1) * (ay1 - by1) - (by2 - by1) * (ax1 - bx1)    v2 = (bx2 - bx1) * (ay2 - by1) - (by2 - by1) * (ax2 - bx1)    v3 = (ax2 - ax1) * (by1 - ay1) - (ay2 - ay1) * (bx1 - ax1)    v4 = (ax2 - ax1) * (by2 - ay1) - (ay2 - ay1) * (bx2 - ax1)    return (v1 * v2 < 0) & (v3 * v4 < 0)#######################################citytmp = []city = []Borderway = []Roadway = []Total = 0A = [0, 0]B = [0, 0]C = [0, 0]D = [0, 0]amount = 0progressbar = 0 ReadyData = open('Готовые данные.txt','w')with open("Города Беларуси.txt", "r", encoding='utf8') as file:    for i in range(115):        citytmp.append(file.readline())citytmp = [line.rstrip() for line in citytmp]for i in range(115):    city.append('"' + citytmp[i] + '"')city[0]='"Дзсна"'api = overpy.Overpass()for number in range(0,115):#главный цикл обработки, перебирает города    borderstring = """(relation["type"="boundary"]["boundary"="administrative"]["name:be"=""" + city[number] + """][place=town]; relation["type"="boundary"]["boundary"="administrative"]["name:be"=""" + city[number] + """][place=city];);>; out skel qt;"""    roadstring = """(area[place=town]["name:be"=""" + city[number] + """]; way["highway"][highway!=service]["highway"!="footway"]["highway"!="track"]["highway"!="path"]    ["highway"!="cycleway"]["highway"!="pedestrian"]["highway"!="steps"]["highway"!="residential"](area);area[place=city]["name:be"=""" + city[number] + """]; way["highway"][highway!=service]["highway"!="footway"]["highway"!="track"]["highway"!="path"]    ["highway"!="cycleway"]["highway"!="pedestrian"]["highway"!="steps"]["highway"!="residential"](area););out body; >; out skel qt;"""    print('Getting data about', city[number],'...')        road = api.query(roadstring)        border = api.query(borderstring)    print('got data!, city:', city[number]) #получаем данные    for w in range(len(border.ways)): #перебирает линии границ        for i in range(len(border.ways[w]._node_ids)):#перебирает узлы линий границ             progressbar = i / len(border.ways[w]._node_ids) * 100            print(progressbar, "%;", w, "of", len(border.ways), "parts ready; city-", city[number])            A[0] = border.ways[w].nodes[i].lon            A[1] = border.ways[w].nodes[i].lat            if i == len(border.ways[w]._node_ids) - 1:                break            B[0] = border.ways[w].nodes[i+1].lon            B[1] = border.ways[w].nodes[i+1].lat            for j in range(len(road.ways)):                for k in range(len(road.ways[j]._node_ids)):                    C[0] = road.ways[j].nodes[k].lon                    C[1] = road.ways[j].nodes[k].lat                    if k == len(road.ways[j]._node_ids) - 1:                        break                    D[0] = road.ways[j].nodes[k+1].lon                    D[1] = road.ways[j].nodes[k+1].lat                    if line_intersection((A, B), (C, D)) == 1:                        amount += 1                        print(road.ways[j]._node_ids[k])    print(amount)    Total += amount * len(city[number])    ReadyData.write(str(city[number]))    ReadyData.write(str(amount))    ReadyData.write('\n')    amount = 0print('Total', Total) #и вывод всего


Заметки по коду


Я достаточно долго составлял запрос, подбирая разные типы дорог чтобы было меньше считать и чтобы не пропустить знаки. В итоговом запросе просто убраны те дороги, на которых нет знаков, например residential, service, footway, track и т. п.
Список городов я спарсил с википедии и сохранил в формате.тхт
Код выполняется достаточно долго, у меня даже один раз возникло желание переписать его на С++, но решил оставить так как есть. У меня он выполнялся два дня, все из-за проблем с диктатурой белорусским интернетом и перегруженностью сервера OverPass. Чтобы решить вторую проблему, нужно составить один запрос ко всем городам, но я еще не придумал как это нормально сделать.

Мой ответ на задачу

18981 буква



Что хочу сказать насчет правильности цифры: все упирается в качество данных самой OSM, то есть там есть места где, например, одну дорогу пересекает две линии границы, или где-нибудь на развязке граница проведена чуть-чуть не так, и в результате имеем лишнее/недостающее пересечение. Но это особенность конкретно этой не имеющей практического смысла задачи, в остальном OSM сила.

Вторая задача


Сейчас давайте посчитаем количество заправок в пределах Москвы:
area[name="Москва"];(  node["amenity"="fuel"](area);  way["amenity"="fuel"](area);  relation["amenity"="fuel"](area););out body;>;out skel qt;

Код
import overpyapi = overpy.Overpass()Data = api.query("""area[name="Москва"];(  node["amenity"="fuel"](area);  way["amenity"="fuel"](area);  relation["amenity"="fuel"](area););out body;>;out skel qt;""")print(len(Data.nodes)) #получаем общее количество узлов


Результат 489 заправок:
Подробнее..

Делаем маршрутизацию (роутинг) на OpenStreetMap. Введение

15.07.2020 20:18:51 | Автор: admin

Хотелось бы поделиться опытом создания систем маршрутизации PostgreSQL/PgRouting на карте OpenStreetMap. Речь пойдет о разработке [коммерческих] решений со сложными требованиями, для более простых проектов, вероятно, достаточно обратиться к документации. Насколько мне известно, такие вещи, как полная поддержка односторонних дорог и направлений движения, быстрый роутинг на тысячах адресов (порядка секунд на обычном лаптопе, к примеру, Macbook Pro 13" 2013 года), создание дорожного графа с заданными свойствами, мета-оптимизация маршрутов вообще нигде и никак не рассматриваются. Как обычно, все данные и результаты доступны в моем GitHub репозитории OSM Routing Tricks, который я буду пополнять по мере публикаций.



Небольшой маршрут из 330 адресов на карте OpenStreetMap (время построения около 5 секунд на вышеупомянутом лаптопе). Можно ли за это же время построить маршрут, скажем, из 5000 точек? Да, можно, и об этом мы тоже поговорим (в следующих частях статьи).


Что такое маршрутизация и зачем она нужна


Пожалуй, легче сказать, где маршрутизация не используется, чем перечислить все ее применения. Например: маршрутизация нужна для доступа к этой статье в сети интернет, для доставки почты или посылок, для путешествий, и даже для спутниковой интерферометрии, о которой я писал ранее, тоже используется маршрутизация (вычисление суммарного сдвига фазы по замкнутым траекториям на растре)! Далее мы будем говорить только о применении расширения PgRouting для СУБД PostgreSQL и карте OpenStreetMap.


В идеале, для поиска оптимального маршрута нам нужно рассмотреть все возможные комбинации из входящих в него адресов. На практике, очевидно, сделать это не удастся для сколько-нибудь значительного количества адресов и потому используются разные приближенные методы. В частности, в расширении PgRouting для поиска оптимального маршрута используется алгоритм отжига. Как и у других методов, у него есть свои преимущества и недостатки, поэтому мы будем обсуждать именно такую реализацию, хотя все или почти все сказанное применимо и к другим методам приближенного поиска оптимального маршрута.


Очень рекомендую также ознакомиться с соответствующей статьей википедии Задача коммивояжёра, поскольку многие старые подходы к задаче и сейчас успешно используются в качестве оптимизаций и решений для частных случаев.


Версии программ и операционные системы


Для новых проектов рекомендую актуальную версию PgRouting 3.0 и СУБД PostgreSQL 12, хотя тот же код обычно работает с PgRouting 2.6 и СУБД PostgreSQL 9 и более давними. Стоит отметить, что на MacOS (любой версии) и Debian/Ubuntu (любой версии) результаты работы функции PgRouting для поиска оптимального маршрута pgr_TSP() сильно отличаются, при этом, как правило, на линуксах результаты при параметрах по умолчанию получаются значительно лучше, при этом используемый алгоритм "отжига" в компиляции на MacOS работает нестабильно, то есть небольшие изменения параметров приводят к непредсказуемым изменениям результатов при недостаточно продуманном дорожном графе, что, кстати сказать, очень помогает при тестировании. Сам я использую и MacOS и Debian/Ubuntu.


Как загрузить OpenStreetMap в базу данных PostgreSQL


Существует много разных утилит, из которых я предпочитаю GDAL, на мой взгляд, наиболее мощную и предсказуемую. Эта утилита позволяет, в частности, преобразовать дамп OpenStreetMap в дамп PostgreSQL и потом загрузить его как обычный SQL скрипт, см. GDAL: PostgreSQL SQL Dump. Можно загрузить и напрямую в базу, см. документацию GDAL. Пример команды загрузки данных из дампа OSM для Германии (germany-latest.osm.pbf) в базу данных PostgreSQL (osmrouting):


ogr2ogr \    -f PGDUMP \    /vsistdout/ "germany-latest.osm.pbf" \    -nln "osm_lines" \    -nlt LINESTRING \    -progress \    --config PG_USE_COPY YES \    --config GEOMETRY_NAME the_geom \    --config OSM_COMPRESS_NODES YES \    --config OSM_CONFIG_FILE "osmconf.ini" \    -sql "select * from lines where highway is not null" \    -lco FID=id \    | psql osmrouting

Дампы OpenStreetMap по странам предоставляет сервис OpenStreetMap Data Extracts.


Как превратить карту дорог OpenStreetMap в дорожную сеть для роутинга


Простейший путь заключается в поиске и нумерации всех пересечений улиц и разбиении всех дорог на участки между найденными точками пересечения. Точки пересечения будут узлами дорожного графа (сети), а участки ребрами этого графа. Узлы необходимы в процессе построения дорожной сети, а для роутинга они не нужны, хотя их удобно использовать для поиска ближайших узлов графа для заданных домов (адресов). В чем минусы такого простого подхода, хорошо описанного в документации? В первую очередь тем, что мы никак не можем ограничить пересечения дорог и полученные маршруты будут в шахматном порядке обходить дома по обе стороны каждой дороги, что выглядит странно, в случае широких улиц такой маршрут очень далек от оптимального и, вдобавок, вовсе не все дороги можно пересечь в произвольном месте. Кроме того, по умолчанию односторонние улицы или полностью исключаются из дорожной сети, или доступны в обоих направлениях. Зато такой вариант очень прост в реализациии для него существуют и специальные утилиты и функции PgRouting.


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



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


Для пешеходных маршрутов лучше для каждого добавленного узла связи с домами также добавить узел к парному ребру (обратного направления) и определить короткие сегменты между этими узлами, так как во многих случаях пешеходу удобнее пересекать небольшие улицы, нежели двигаться по одной стороне от перекрестка до перекрестка. Для автотранспортных маршрутов такой вариант тоже подходит, только нужно увеличить расстояние между виртуальными узлами. Скажем, 10 м для пешеходного маршрута и 100 м для автомобильного разумная оценка для каждой смены направления вне перекрестка.


Возможно, вас интересует вопрос, почему мы тратим время на создание сложной дорожной сети вместо использования более продвинутых методов построения оптимального маршрута? Все очень просто по хорошей дорожной сети даже простой алгоритм быстро (секунды) построит отличные маршруты, в то время как по посредственной дорожной сети даже лучшие из существующих алгоритмов за разумное время (минуты, часы или дни, в зависимости от задачи) построят плохие маршруты или вообще не смогут завершить вычисления. Это следствие невероятной вычислительной сложности задачи. К примеру, если мы желаем посетить 10 или 100 адресов по обе стороны одной улицы и при этом у нас определены два направления движения и только два перехода между ними, скажем, в начале и в конце улицы задача имеет единственное решение и любой алгоритм его найдет почти мгновенно! В случае же, когда у нас нет заданных направлений движения и разрешено пересечение улицы около каждого заданного адреса задача становится не решаемой вычислительно уже для нескольких десятков адресов и разные алгоритмы и для разного числа адресов вернут разные и весьма не оптимальные маршруты. Таким образом, критически важно именно построить дорожную сеть с такими ограничениями, которые запрещают большинство (неоптимальных) маршрутов, так что пространство перебора становится несравнимо меньше и достаточно оптимальный результат гарантирован любым из методов. Именно ограничения на направление движения и допустимые повороты являются самыми эффективными.


Таблицы базы данных PostgreSQL


Мы будем работать с базой "osmrouting", содержащей несколько таблиц, содержимое которых доступно в виде PostgreSQL SQL дампов в репозитории, см. также скрипт инициализации базы данных и загрузки дампов load.sh (также загружает необходимые расширения PgRouting и PostGIS).


Первая таблица является единственной необходимой и содержит данные маршрутной сети и, по желанию, информацию для визуализации (сам алгоритм роутинга работает с идентификаторами ребер графа start_id и end_id и некоторой стоимостью каждого сегмента, задаваемой, например, его длиной length):


-- таблица с ребрами маршрутного графа (сегментами дорожной сети)-- id - идентификатор для отладки-- the_geom - геометрия дорожного сегмента для визуализации-- oneway - флаг односторонней дороги-- highway - флаг скоростного шоссе (можно запретить на нем повороты и пересечения)-- pedestrian - флаг пешеходной улицы (можно запретить движение транспорта)-- start_id - идетификатор стартового узла сегмента для роутинга-- end_id - идетификатор конечного узла сегмента для роутинга-- length - длина сегмента (может переопределяться в зависимости от типа дороги и проч.)osmrouting=# \d osm_network                       Table "public.osm_network"   Column   |           Type            | Collation | Nullable | Default ------------+---------------------------+-----------+----------+--------- id         | integer                   |           |          |  the_geom   | geometry(LineString,4326) |           |          |  type       | text                      |           |          |  oneway     | boolean                   |           |          |  highway    | boolean                   |           |          |  pedestrian | boolean                   |           |          |  start_id   | bigint                    |           |          |  end_id     | bigint                    |           |          |  length     | double precision          |           |          | 

Вторая таблица не является необходимой и используется только для поиска ближайшего узла дорожной сети для заданных адресов:


-- таблица с узлами маршрутного графа-- id - идентификатор для отладки-- the_geom - геометрия дорожного узла для визуализацииosmrouting=# \d osm_nodes                     Table "public.osm_nodes"  Column  |         Type         | Collation | Nullable | Default ----------+----------------------+-----------+----------+--------- id       | bigint               |           |          |  the_geom | geometry(Point,4326) |           |          | 

Третья таблица также не является необходимой и используется только для хранения адресов и координат домов, которые мы используем в тестовых скриптах:


osmrouting=# \d osm_buildings                   Table "public.osm_buildings"  Column  |         Type         | Collation | Nullable | Default ----------+----------------------+-----------+----------+--------- id       | character varying    |           |          |  the_geom | geometry(Point,4326) |           |          | ...

Также тестовые скрипты создают дополнительные таблицы.


Поиск оптимального маршрута


Скрипт репозитория route.sql содержит необходимые команды для выбора случайных 330 адресов домов и построения маршрута по ним с помощью функции PgRouting pgr_TSP(). На самом деле, указанная функция работает не с координатами, а с матрицей расстояний (есть функции и для работы с координатами, см. документацию PgRouting). Матрица расстояний может быть создана функцией pgr_dijkstraCostMatrix(). Заметим, что в скрипте для генерации этой матрицы указан флаг directed=false, так как по умолчанию pgr_TSP() не работает с односторонними дорогами (точнее, не работает с несимметричной матрицей, которая получается при наличии односторонних дорог). С помощью моих функций pgr_dijkstraSymmetrizeCostMatrix() и pgr_dijkstraValidateCostMatrix() это ограничение можно обойти, как мы увидим далее. Что интересно, маршрут возвращается в виде списка идентификаторов узлов дорожного графа (в нашем случае все узлы результата соответствуют добавленным нами виртуальным узлам для домов) и для получения маршрута в виде линии нужно полученный список идентификаторов передать в функцию pgr_dijkstraVia() для нахождения всех посещенных ребер дорожной сети в нужном порядке.


Параметр randomize=false обеспечивает выполнение нескольких запусков алгоритма роутинга и возвращение лучшего результата, для целей отладки и ускорения вычислений можно указать randomize=true, но возвращаемый результат в таком случае непредсказуем. В результате выполнения указанного SQL скрипта создается таблица "route" с сегментами маршрута для каждого заданного адреса, которую можно визуализировать с помощью программы QGIS. Файл проекта QGIS также представлен в репозитории, см. route.qgs Полученная карта с маршрутом представлена на картинке до хабраката.


Смотрите на следующем рисунке участок дорожной сети с узлами и построенного маршрута с порядковыми номерами посещенных домов:



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


Заключение


Сегодня мы обсудили самые основы как получить исходные данные, подготовить маршрутную сеть и выполнить поиск оптимального маршрута средствами PgRouting. Для того, чтобы улучшить полученный маршрут (как минимум, двигаться по дорогам в правильном направлении), нам понадобится добавить некоторые свои функции, чем мы и займемся в следующей части статьи. Далее поговорим о выборе параметров маршрутизации и других улучшениях.

Подробнее..

Делаем маршрутизацию (роутинг) на OpenStreetMap. Добавляем поддержку односторонних дорог

27.07.2020 10:17:57 | Автор: admin

Продолжаем цикл статей про построение систем роутинга со сложными требованиями на основе Open Source базы данных PostgreSQL и расширения PgRouting на карте OpenStreetMap. Сегодня мы поговорим о том, как добавить поддержку односторонних дорог (направлений движения). Зачастую, именно отсутствие этой поддержки является основной причиной смены PgRouting на другой "движок" маршрутизации. Как обычно, все данные и результаты доступны в моем GitHub репозитории OSM Routing Tricks, который я пополняю по мере публикаций.



Небольшой маршрут из 330 адресов на карте OpenStreetMap.


Что такое односторонние дороги и зачем они нужны


В первую очередь, мы говорим об односторонних дорогах на карте, то есть таких, по которым движение разрешено только в одну сторону. Разумеется, двигаться по ним в другую сторону запрещено. Особенно часто такое ограничение встречается на мостах, в туннелях и на высокоскоростных шоссе, где движение в противоположную сторону опасно для жизни. Разумеется, нам необходимо соблюдать направление движения для автотранспортных средств, здесь не может быть компромиссов. В то же время, для пешеходного роутинга это не является обязательным, хотя и удобно двигаться по маршруту по направлению движения автомобилей проще ориентироваться, можно воспользоваться общественным транспортом или такси и т.п. Замечу, что некоторые (многие, на самом деле) открытые системы роутинга игнорируют это правило, то есть они подразумевают пешеходный роутинг (по пешеходным дорогам и по окружающим не пешеходные дороги тротуарам, которые могут и отсутствовать в действительности), независимо от длины маршрута и наличия тротуаров в туннелях и на скоростных шоссе.
Кроме того, поддержка односторонних дорог позволяет улучшить маршрутную сеть заданные направления движения заметно упрощают поиск оптимального маршрута за счет очень значительного снижения количества возможных вариантов, можно учесть рельеф местности (довольно трудно с грузом или без подняться по множеству ступенек крутой лестницы, особенно, если можно вместо того спускаться по ней) и принудительно сделать некоторые участки односторонними, можно сделать односторонними виртуальные соединения между домами и улицами (так что маршрут будет построен с последовательной нумерацией, даже если он несколько раз проходит по одной улице поскольку длина маршрута в таком случае не меняется, сам PgRouting никак не гарантирует, что все адреса на такой улице будут посещены в один и тот же проход по ней). И так далее, есть еще много возможностей, доступных для роутинга с поддержкой направлений движения.


Добавляем поддержку односторонних дорог в PgRouting


Официальная документация PgRouting для функции pgr_TSP Using Simulated Annealing approximation algorithm сообщает нам:


If using directed := true, the resulting non symmetric matrix must be converted to symmetric by fixing the non symmetric values according to your application needs.

Отлично, но в документации нет ни слова о том, как это сделать. Нам придется начать с теории и разобраться, возможно ли это и как именно это сделать. Англоязычная страница википедии, посвященная проблеме коммивояжера, содержит нужный нам раздел Travelling salesman problem: Asymmetric:


Solving an asymmetric TSP graph can be somewhat complex. The following is a 33 matrix containing all possible path weights between the nodes A, B and C. One option is to turn an asymmetric matrix of size N into a symmetric matrix of size 2N.

Здесь сказано, что решение задачи коммивояжера на асимметричной матрице расстояний усложняется, поэтому лучше превратить асимметричную матрицу в симметричную (ценой удвоения размера матрицы) и решать более простую симметричную задачу на симметричной матрице расстояний. Почти то же самое, что и в документации PgRouting, зато здесь объяснено, зачем же нужна именно симметричная матрица. Далее на этой же викистранице приводится алгоритм конвертации асимметричной матрицы в симметричную и пример конвертации простой матрицы. Идея простая каждый узел и каждое ребро заменить на два и задать такую матрицу расстояний между ними, чтобы полученный на такой матрице путь соответствовал искомому. Ниже мы рассмотрим этот пример из вики. К сожалению, графическое представление соответствующего графа я рисовал на листочках от руки и показать здесь не могу (почерк у меня как у физика, так что извините).


Пусть у нас задана асимметричная матрица весов:


A B C
A 1 2
B 6 3
C 5 4

Ей соответствует следующая симметричная матрица весов:


A B C A' B' C'
A -w 6 5
B 1 -w 4
C 2 3 -w
A' -w 1 2
B' 6 -w 3
C' 5 4 -w

где -w это виртуальные соединения для виртуальных узлов, которые рекомендуется задать некоторым отрицательным числом, потому что


w=0 is not always low enough

Теперь мы можем написать соответствующую функцию-обертку для PgRouting. Поскольку PgRouting отбрасывает все отрицательные значения в матрице весов, мы используем нулевое значение (мы это компенсируем заданием очень высоких весов для запрещенных направлений) и добавляем еще [избыточные] ребра с очень высоким весом, поскольку PgRouting требует указания весов между всеми узлами матрицы (в теории этого не требуется, это просто техническое ограничение, которому нам приходится соответствовать).


CREATE OR REPLACE FUNCTION pgr_dijkstraSymmetrizeCostMatrix(matrix_cell_sql text,    OUT start_vid BIGINT, OUT end_vid BIGINT, OUT agg_cost float)RETURNS SETOF RECORD AS$BODY$DECLARE    sql text;    r record;BEGIN    sql := 'with edges as (' || matrix_cell_sql || ')        select 3e9+start_vid as start_vid, end_vid as end_vid, agg_cost from edges        union        select end_vid, 3e9+start_vid, agg_cost from edges        union        select 3e9+start_vid, start_vid, 0 from edges        union        select start_vid, 3e9+start_vid, 0 from edges        union        select start_vid, end_vid, 1e6 from edges        union        select 3e9+start_vid, 3e9+end_vid, 1e6 from edges        ';    FOR r IN EXECUTE sql LOOP        start_vid := r.start_vid;        end_vid   := r.end_vid;        agg_cost  := r.agg_cost;        RETURN NEXT;    END LOOP;END;$BODY$LANGUAGE plpgsql VOLATILE STRICT;

Однако, этого недостаточно. Дело в том, что PgRouting требует указания всех весов, а у нас в исходной матрице весов часть значений может быть пропущено, что приводит к ошибке роутинга


An Infinity value was found on the Matrix

Заполним все значения матрицы весов с помощью еще одной функции-обертки (можно было бы упростить pgr_dijkstraSymmetrizeCostMatrix() и не добавлять в ней избыточные ребра, но в таком случае при полной исходной матрице мы получим не полную итоговую матрицу, что, на мой взгляд, не есть правильно):


CREATE OR REPLACE FUNCTION pgr_dijkstraValidateCostMatrix(matrix_cell_sql text,    OUT start_vid BIGINT, OUT end_vid BIGINT, OUT agg_cost float)RETURNS SETOF RECORD AS$BODY$DECLARE    sql text;    r record;BEGIN    sql := 'WITH RECURSIVE src AS (' || matrix_cell_sql || '),    dst AS (        select            *        from src where start_vid =            (select                start_vid            from src            group by start_vid            order by count(*) desc            limit 1)        union        select            src.*        from src, dst        where src.start_vid=dst.end_vid    )    select * from dst';    FOR r IN EXECUTE sql LOOP        start_vid := r.start_vid;        end_vid   := r.end_vid;        agg_cost  := r.agg_cost;        RETURN NEXT;    END LOOP;END;$BODY$LANGUAGE plpgsql VOLATILE STRICT;

Теперь мы учли все требования PgRouting и можем работать с односторонними дорогами. Отмечу, что полученные обертки можно использовать и для симметричной матрицы, когда априори не известно, будет ли исходная матрица симметричной (хотя для симметричной исходной матрицы это приведет к не нужному ее увеличению и замедлению обработки).


Исходные данные


Мы используем все те же данные, что и в предыдущей части статьи плюс определенные выше дополнительные функции. Несколько расширенный скрипт load.sh из репозитория загрузит в базу данных PostgreSQL все необходимое.


Поиск оптимального маршрута для автомобиля


Поскольку для пешехода тротуары доступны в любом направлении, а для автомобиля одностонние дороги доступны лишь в одном направлении, необходимо указывать разные весовые функции для роутинга пешеходного и автомобильного. Как уже упоминалось ранее, наша дорожная сеть оптимизирована для автомобильного роутинга, о нем и поговорим. Итак, запретим (укажем очень большую длину миллион метров) движение в противоположном направлении. Ранее при подготовке дорожной сети мы разделили каждую дорогу на две, одна из которых (type='osm') совпадает с исходной и вторая (type='osmreverse') инвертирована. Соответственно, для односторонней дороги добавленная нами ее инвертированная копия должна быть запрещена для любого движения (вообще говоря, можно ее и вовсе не создавать, но тогда будет немного сложнее объяснить построение дорожной сети). Кроме того, для прямого движения должна быть доступна только исходная дорога (type='osm') и для обратного инвертированная (type='osmreverse'). В таком случае, итоговые прямая и обратная весовые функции имеют вид:


    case when (type='osmreverse' and oneway) then 1000000 else length end as cost,    case when type ilike 'osm%' then 1000000 else length end as reverse_cost,

где length геометрическая длина соответствующего сегмента дороги. Усложнив дорожную сеть (заранее исключив ненужные сегменты), можно взамен упростить весовые функции.


Построим оптимальный маршрут все для тех же случайных 330 адресов домов и с помощью функции PgRouting pgr_TSP() и добавленных выше функций pgr_dijkstraSymmetrizeCostMatrix() и pgr_dijkstraValidateCostMatrix(). Теперь мы можем использовать флаг directed=true, так как теперь мы добавили для pgr_TSP() поддержку односторонних дорог (точнее, симметризацию и заполнение пропущенных значений в несимметричной матрице, которая получается при наличии односторонних дорог). Как и ранее, в результате выполнения немного модифицированного SQL скрипта route.sql создается таблица "route" с сегментами маршрута для каждого заданного адреса, которую можно визуализировать с помощью программы QGIS. Файл проекта QGIS тоже обновлен, см. в репозитории route.qgs Полученная карта с маршрутом представлена на картинке до хабраката.


Смотрите на следующем рисунке участок построенного маршрута с порядковыми номерами посещенных домов (здесь черным цветом обведены односторонние дороги):



Проверим, что теперь по односторонним улицам движение выполняется только одну сторону и, притом, в требуемом направлении. Вот карта OpenStreetMap, на которой стрелками указаны направления движения по односторонним дорогам:



Как и указано на карте OpenStreetMap, в построенном маршруте движение выполняется слева направо для участка дороги с пунктами маршрута 329,330,331 на левой стороне дороги:



и в том же направлении (слева направо) для участка дороги с пунктами маршрута 72,73,74 на правой стороне дороги (второй проход по этому участку дороги):



Нумерация домов по сторонам улиц между перекрестками последовательная. Зачастую нумерация последовательная и после перекрестков (см. картинки выше). Таким образом, мы добились заметного улучшения качества маршрута, не меняя дорожную сеть и не занимаясь оптимизацией параметров функции pgr_TSP().


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


Заключение


В этой статье мы улучшили возможности PgRouting, добавив к нему несколько своих функций. Теперь мы можем двигаться по дорогам в правильном направлении, что необходимо для построения реальных маршрутов и позволяет значительно улучшить итоговый маршрут, поскольку при этом количество возможных маршрутов кардинально снижается.


Если рассмотреть весь маршрут детально, то его еще можно улучшить, в том числе, с помощью оптимизации параметров функции pgr_TSP(). Но дело в том, что только путем оптимизации этих параметров подобного полученному выше результату добиться не удастся в самом деле, алгоритм оптимизации маршрутов PgRouting ничего "не знает" про наши предпочтения о последовательной нумерации и другие. Так что поддержка односторонних дорог, на самом деле, дает нам намного больше, чем можно было бы ожидать.


Можно ли добиться аналогичного результата с последовательной нумерацией зданий между перекрестками иначе, не увеличивая матрицу весов с соответствующим замедлением построения маршрута? Да, можно. Более того, можно еще и значительно ускорить построение маршрута (например, на один-два десятичных порядка), в том числе, с учетом односторонних дорог. Об этом мы и поговорим в следующий раз.

Подробнее..

Владельцы MAPS.ME отменили изменения и вернули старое приложение. Надолго ли?

01.02.2021 12:14:39 | Автор: admin

Слева старое приложение, справа декабрьская версия от южнокорейцев. Источник: Смерть MAPS.ME?

В ноябре 2020 года Mail.Ru Group продала MAPS.ME южнокорейской компании Daegu Limited (входит в состав платёжной системы Parity.com), и уже 20 декабря 2020 года новые владельцы выпустили обновление, которое практически убило приложение.

Много лет это бесплатное приложение для мобильных устройств работало на свободных картах OpenStreetMap, отличалось великолепной производительностью и маленьким размером карт, но новые хозяева его переписали. На скриншотах вверху сравниваются карты одного и того же места.

К счастью, новые владельцы осознали глубину своей ошибки и откатили изменения. В апдейте от 30 декабря 2020 года восстановлена вся функциональность. Казалось бы, победа? Справедливость восторжествовала? Нет, в это слабо верится.

Продажа MAPS.ME


Приложение MAPS.ME (первоначально MapsWithMe) создал белорусский стартап MapsWithMe в апреле 2011 года. Разработчиков было четверо Юрий Мельничек (автор проекта, бывший сотрудник Google), Александр Золотарёв, Виктор Говако и Сергей Речицкий.


Юрий Мельничек, Александр Золотарёв, Виктор Говако и Сергей Речицкий в 2011 году. Источник: 42.tut.by

Изначальной идеей было выпустить карты, которые работают в офлайне, в отличие от Google Maps. Во-первых, они доступны без интернета и не нужно платить за роуминг. Во-вторых, такое приложение по своей сути будет быстрее. Идея оказалась отличной. Хотя к тому времени на рынке уже было небольших приложений с картами в офлайне, но большинство из них сохраняли данные в виде статических изображений, а это не позволяло скачать и использовать целиком карту страны в виде векторных тайлов.

За основу сразу взяли OpenStreetMap, но в приложении изменили стили, чтобы карты OSM стали удобнее и симпатичнее. Впоследствии ссылку на OpenStreetMap спрятали, что можно считать нарушением лицензии OSM.

Команда работала над проектом примерно полтора года. Было тяжело, инвестиции из собственного кармана достигли $250 тыс. Двум основателям пришлось продать свои квартиры. Первой вышла версия для iOS, потом для Android. Затем платная версия за 169 рублей, которая позволяла загружать карты в высоком разрешении. Приложение нашло аудиторию по всему миру: на первом месте по количеству пользователей были США, потом Россия.

В 2014 году компанию купила корпорация Mail.Ru Group за 542 млн рублей ($11,48 млн по курсу на 14 ноября 2014 года) и основатели получили свои доли. Приложение стало бесплатным.

Сразу после покупки был закрыт допотопный фирменный сервис Карты@Mail.ru. Вместо него планировалось везде использовать MAPS.ME, но не получилось. Он остался независимым мобильным приложением. В остальных продуктах (доставка, такси) Мейл по-прежнему использует карты Google Maps, которые стоят огромных денег.

Нужно заметить, что автономия проекта MAPS.ME в составе Mail.Ru была одним из условий его продажи. И его не получилось никуда интегрировать.

Может, оно и к лучшему, потому что тогда Mail.Ru мог бы и прибить отдельное мобильное приложение.

К 2019 году приложение преодолело отметку в 140 млн скачиваний и 10 млн месячных активных пользователей. Выручка составила около $2,5 млн, чистый убыток около $385 тыс. Команда разрослась, набрали менеджеров и т. д.


Команда MAPS.ME в Mail.Ru, источник

По мнению пользователей, с 2014 года приложение не стало хуже, что является заслугой разработчиков Mail.Ru. Они его не испортили. Хотя пытались. Например, в 2019 году сервис запустил платную подписку на каталог путеводителей, которые бесплатно доступны в других местах это не очень красиво.

В ноябре 2020 года MAPS.ME решили продать корейской компании Daegu Limited (входит в состав платёжной системы Parity.com) за 1,557 млрд рублей, это $19,33 млн по курсу на 2 ноября 2020 года. К тому времени компанию давно покинули основатели, а проект до самого конца оставался убыточным.

Возможно, финансовые показатели стали одной из причин продажи. К тому же, у совместного предприятия Сбера и Mail.ru есть доля в картографическом сервисе 2ГИС, который Сбербанк целиком купил в июне 2020 года. Кроме того, и OpenStreetMap, и MAPS.ME опенсорсные проекты, и кто угодно может создавать продукты на их основе. Сейчас Mail.Ru начала работать над собственными сервисами картографии на основе OpenStreetMap.

Что изменилось в декабре 2020 года


Для начала вспомним этот ужас, который появился в декабрьском обновлении. Создалось ощущение, что новые владельцы из Южной Кореи понятия не имеют, что за программа попала им в руки, как она работает и за что её любят миллионы человек.

Небольшой список сделанных изменений:

  • Для получения карт внедрили Mapbox SDK. В результате карты практически перестали работать в офлайне
  • Фоновое скачивание работало плохо
  • Пропали карты метро и навигация по метро
  • Испортилась пешеходная навигация, исчезла диаграмма с перепадом высот
  • Пропали личные данные: подписки на отключение рекламы, отзывы, записи пройденных маршрутов
  • Интерфейс стал монструозным, сильно напоминая Google Maps. Размер карт увеличился в четыре раза, при этом ухудшилась детализация. Например, размер карт Москвы вырос с 50 до 200 МБ
  • Исчез ландшафтный режим
  • Приложение стало тормозить

Зато появился кошелёк. Ведь новый владелец это платёжная система.

Для разработчиков MAPS.ME выход новой версии оказался сюрпризом.

Разразился большой скандал, поэтому вскоре изменения откатили и вернули оригинальную версию.

Но есть основания полагать, что корейцы не просто так переделали приложение и поэтому могут со временем вернуться к изначальному плану. Ведь нужно понимать, что проект убыточный. Если приложение бесплатное, то остаётся монетизация через рекламу. Кафе, рестораны и магазины платят за баннеры прямо на картах. Чтобы реализовать такую модель, разработчикам нужна удобная платформа, куда будет интегрироваться эта реклама. Вероятно, с этой целью они и переделали приложение.

Поэтому существует ненулевая вероятность, что ситуация повторится. Изменения просто ненадолго откатили, а потом Daegu Limited снова вернётся к первоначальному плану. Деньги есть деньги.

Форки MAPS.ME


К счастью, в 2015 году Mail.Ru сделала хорошее дело и открыла исходный код на GitHub под лицензией Apache 2.0 для свободного использования (официальный репозиторий). Его можно использовать частично (например, для добавления отображения офлайн-карт) или целиком, для создания нового сервиса с помощью модификации Maps.me.

Репозиторий форкнули более 1100 пользователей GitHub. Это гарантирует сохранность исходного кода.

Благодаря открытым исходникам, вскоре после продажи проекта был запущен проект по восстановлению оригинального облика MAPS.ME. В телеграм-канал Original MAPS.ME Project добавилось более 250 человек.

И уже много лет поддерживается свободный форк F-Droid.

Хотя инициатива с форками сейчас не так актуальна, потому что корейцы откатили изменения и вернули оригинальную версию MAPS.ME, но какая-то работа всё равно идёт, судя по активности в телеграм-канале и репозиториях. Например, разработчики заменяют иконки, на которые может распространяться копирайт. Они заменили уже 100 из 400 иконок.



Но если владельцы MAPS.ME опять испортят приложение, можно какое-то время не устанавливать новую версию. Просто скачать все нужные карты и пользоваться старым приложением. Форк исходного кода гарантирует, что эта версия приложения никуда не исчезнет.

Альтернативы MAPS.ME


На самом деле MAPS.ME это просто одна из многих оболочек для рендеринга OpenStreetMap, и вся суть именно в свободных картах, которые редактируются силами сообщества. Туда люди вносят тропинки и другие объекты, которые вы никогда не найдёте на Google Maps. Лучшая информативность OSM по сравнению с Google Maps особенно хорошо заметна в труднодоступных местах и не очень популярных туристических маршрутах.

Вообще, карты Google Maps предназначены в первую очередь для автомобилистов и обслуживания бизнесов. А для прогулок по городу, туризма, велосипедного спорта и любого другого активного отдыха OpenStreetMap просто без конкуренции.


Для сравнения, одна и та же лесная зона в черте города. Слева OpenStreetMap (MAPS.ME), справа Google Maps. Источник

Чтобы карты отображались в офлайне, нам нужны векторные тайлы и программа для их рендеринга.

OpenStreetMap главная причина популярности MAPS.ME. Но OSM это по сути база геоинформационных данных, а не векторные тайлы. Чтобы сгенерировать тайлы, требуется специальный движок, так как MapTiler, Mapbox или опенсорсный OpenMapTiles (на базе MapTiler), который можно установить на своём сервере и пользоваться им бесплатно. Этот сервер подготавливает векторные тайлы для загрузки в мобильное приложение.

И хорошая новость: есть целый ряд альтернативных программ для рендеринга тайлов. Вот небольшой список мобильных приложений, которые импортируют данные из OpenStreetMap, загружают тайлы на устройство и работают в офлайне. Хотя приложения основаны на свободных картах, некоторые из них платные или продают платный контент:

  • OsmAnd, есть бесплатная и платная версии (треки и тропинки, поддерживаются тепловые карты Strava и тайлы OpenTopo), не требует создания аккаунта, поддерживает закладки, работает в офлайне, отличается насыщенным UI, iOS, Android. В последних версиях под Android исправлен баг с поиском, из-за которого раньше поиск на карте практически не работал. Если кому-то интересно, вот телеграм-канал с разработчиками.


    OsmAnd для Android
  • Mapy.cz (есть популярные трейлы, горизонтали высот, хорошо подходит для велоспорта), работает в офлайне, Android-версия Mapy.cz Cycling & Hiking offline maps, также версии под iOS, Windows и AppGallery. Это приложение фокусируется на Центральной Европе, а международная версия называется Windy Maps
  • Guru Maps
  • Bike Citizens
  • Naviki
  • MapFactor Navigator
  • Pocket Earth, только для iOS
  • MapOut, только для iOS


    3D-эффект в MapOut
  • Gaia GPS, походы и путешествия

Что касается альтернатив, то одной из лучших считают OsmAnd. Здесь поддерживаются GPS-навигатор, предупреждения о камерах контроля скорости (POI) и другие необходимые функции. Правда, некоторые функции платные, хотя при желании можно сэкономить. Например, вот бесплатная версия от F-Droid (3.8.5) и бесплатные карты. Карты скачиваются заранее и хранятся на устройстве, как и положено. Есть мнение, что карты OsmAnd даже полнее и информативнее, чем у MAPS.ME.



Итак, старое приложение MAPS.ME пока остаётся с нами. Но если что-то пойдёт не так мы знаем, что делать.

P. S. Кстати, в картографических магазинах ещё продаются бумажные карты разных масштабов. Иногда в походе такая карта удобнее, чем экран мобильного телефона, хотя в 21 веке это уже какой-то экстремальный дауншифтинг.



На правах рекламы


Наша компания предлагает серверы не только с CPU от Intel, но и серверы с процессорами AMD EPYC. Как и для других типов серверов, огромный выбор операционных систем для автоматической установки, есть возможность установить любую ОС с собственного образа. Попробуйте прямо сейчас!

Подробнее..

Человеку надо мало чтоб искал и находил

16.02.2021 16:13:42 | Автор: admin


Нет, это не очередная реклама одного известного автомобильного бренда. И даже не попытка литературного разбора стихотворения Рождественского. Речь пойдет о картах. Мы в Mail.ru Group продолжаем развивать собственные геосервисы как для нужд внутренних проектов, так и для запуска новых продуктов для широкого круга пользователей. Ядром географических данных для наших сервисов выступают данные картографического проекта OpenStreetMap. Но мы не хотим быть просто ленивыми потребителями, которые, получая обратную связь от пользователей, хранят исправления в локальной базе данных и не делятся ими с общественностью. Мы активно вносим свой вклад в проект OpenStreetMap. В цикле статей мы решили рассказать, как корректно вносить правки о наиболее насущных для нас объектах географической действительности.

О проекте


OpenStreetMap работает по принципу вики, то есть каждый зарегистрированный пользователь может вносить свои правки в карту. После регистрации становится доступным богатый мир сообщества OpenStreetMap.



Прежде чем рисовать что-то на карте, необходимо ознакомиться со спецификой представления информации в OpenStreetMap объекты здесь представлены в виде точек или узлов (node), линий или путей (way), и отношений (relation). Атрибутивная информация описывается в виде тегов, представляющих собой пару ключ-значение.

О редакторах


Для редактирования данных в OpenStreetMap существует множество редакторов для различных платформ. Наиболее распространенными из немобильных редакторов в OSM-сообществе являются веб-редактор iD, который сейчас рассмотрим подробно, и настольный редактор JOSM, который рассмотрим в следующей статье.

Новичкам, не имеющим опыта работы в ГИС-редакторах и немного знающих теги OSM, подойдет редактор iD. В нем удобно оцифровывать белые пятна и редактировать теги объектов. При создании и редактировании объекта в левой части экрана появляется его карточка, хранящая информацию о тегах.


Набор тегов для карточки сформирован в соответствии с типом объекта, указанным при его создании. Например, при добавлении бара в карточке появятся поля для указания его названия, адреса, времени работы (отдельно поле для часов работы во время пандемии), минимальном возрасте посетителей. А при добавлении болота в карточке появятся поля для указания его типа, солености воды и приливных явлений.

Более продвинутым пользователям может понравиться редактор JOSM, который мы рассмотрим подробнее в следующей статье. В JOSM тоже есть шаблоны для рисования объектов определенных категорий, однако нужно быть знакомым со схемой тегирования OpenStreetMap. Она описана на вики-портале, где также есть ссылки на форумы и Telegram-каналы активного русскоязычного OSM-сообщества.

Об источниках данных


Поскольку данные OpenStreetMap могут быть использованы в коммерческих целях, то и к источникам данных применяются строгие требования: их использование должно быть явно разрешено правообладателем. Так, например, не допускается использование данных панорам Google Maps, но разрешено использование панорам Яндекс.Карт. Недавно сообщество договорилось об использовании данных об адресах из слоев группы Общедоступные сведения, содержащиеся в ЕГРН публичной кадастровой карты. Также к правомерным источникам относятся фотографии сервиса Mapillary.

Самым очевидным способом получить данные, легитимные для использования в OpenStreetMap, является обход/объезд местности. Тогда обходчик становится правообладателем данных, которые он собрал, и со спокойной совестью может нанести их на карту. Один из глобальных источников данных спутниковые снимки. При их использовании стоит помнить, что из-за особенностей съемки и рельефа земной поверхности, снимок может быть смещен относительно реального положения объектов на местности. Тут на помощь придут GPS-треки, загружаемые пользователями проекта. Возможность подгрузить треки есть как в редакторе iD, так и в редакторе JOSM. Подгрузив треки, можно настроить корректное смещение снимка; проще всего это делать по перекресткам дорог, выбирая перекрестки с большим количеством треков и сопоставляя центральную ось пучка треков с осью дороги на снимке.



Также про смещение на снимке стоит помнить при оцифровке многоэтажных зданий: очень часто крыша здания может быть смещена относительно его основания. Именно положение основания здания на снимке соответствует положению здания на местности.


Один из ключевых постулатов OpenStreetMap не картировать под рендер. То есть не создавать объекты с неопределенным значением тега только лишь для того, чтобы они отображались на карте. Нарисованный объект должен нести в себе максимум информации, описывающей его.

Редактирование OpenStreetMap средствами iD


iD является редактором по умолчанию. Именно он открывается при клике на кнопку Правка на странице карты OpenStreetMap. Перед началом редактирования пользователю предлагается пройти обучение.


Обучение интерактивное, а обучающие материалы позволяют пользователю любого уровня достаточно быстро освоить базовые операции в iD. Пожалуй, большинство новичков начинает свой вклад в OpenStreetMap с добавления информации о собственном доме. Подробно рассмотрим этот процесс с помощью редактора iD.

Для начала настроим среду для работы. У правого края экрана расположены кнопки управления редактором. Наиболее используемые две верхние кнопки в нижнем блоке: Настройки фонового изображения и Данные карты.


По клику на Настройки фонового изображения открывается панель с настройками. В этом же разделе настраивается смещение спутниковых снимков, о важности которого написано выше. Панель Данные карты позволяет настроить стиль отображения объектов OpenStreetMap. Например, объекты можно скрыть, сняв флаг Данные OpenStreetMap, чтобы подробнее рассмотреть фоновое изображение. Настроим смещения снимков, активировав флаг GPS-треки OpenStreetMap в разделе Накладываемые изображения панели Фоновое изображение. Прокрутив вниз панель Фоновое изображение, найдем раздел Смещение снимка, где, перетаскивая серую область, манипулируем спутниковым изображением, подстраивая его под GPS-треки.


Теперь рабочая среда настроена. Можно начинать рисовать. Здания являются полигональными объектами, поэтому для начала рисования нажимаем кнопку Полигон. Обводим здание по контуру, учитывая наклон строения на снимке. Двойной щелчок левой кнопки мыши завершает редактирование. Укажем тип объекта, и в обновившейся карточке добавим необходимую информацию.

Стоит обратить внимание на раздел Адресная информация в карточке дома. При указании номера дома, состоящего не только из цифр, следует руководствоваться следующими правилами: литеры пишутся слитно с номером дома, а номера корпусов, строений, сооружений, владений и т.п. указываются через пробел.


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


Но как быть, если наименования нет в списке? Для начала осмотрите окружающие объекты карты. Если нужная улица уже нарисована, то стоит проверить ее теги, возможно, ей присвоено некорректное название, либо оно и вовсе не заполнено. А если улица не нарисована, необходимо ее нарисовать, присвоив корректный тип объекта. Скорее всего, это будет тип Улица, так как большинство основных дорог уже нарисовано. Теперь необходимо добавить название улицы в соответствии с принятым в русскоязычном сообществе OpenStreetMap соглашением.

Коротко об основных правилах соглашения: наименование состоит из статусной части, определяющей элемент улично-дорожной сети, например, улица, проспект, переулок, бульвар, и основной части, характеризующей название. Статусная часть должна быть указана полностью, без сокращений, со строчной буквы. Статусная часть указывается перед основной частью, если основная часть представляет собой существительное или словосочетание, управляемое существительным (улица Клин, улица Морские Дубки), а также группу слов в родительном падеже (проспект Ленина, улица Подольских Курсантов), или группу слов, управляемую предлогом (дорога в Каменку). В случае, если основная часть представляет собой прилагательное в именительном падеже, статусная часть указывается после основной (Невский проспект, Рябиновая улица).

Итак, с адресной информацией разобрались. Также в карточке объекта можно указать данные об этажности поле Число этажей, высоте поле Высота (метры), а в поле Units указывается количество жилых помещений (квартир) в здании. Вернемся к редактированию формы нашего дома. Выйти из режима редактирования тегов можно с помощью клавиши Esc, либо нажатием на кнопку с крестиком в карточке объекта.

Скорректируем форму только что нарисованного дома, нажав на него правой кнопкой мыши, и в контекстном меню выбрав кнопку Выпрямить углы.


Следует обратить внимание, что тип здания указывается в соответствии с его назначением, а не текущим использованием. Например, здание, построенное как склад, в настоящее время используется как жилой дом. В этом случае тип здания Склад, так как предназначалось оно именно под складские помещения, а текущее использование отображается дополнительным тегом building:use=residential.


Если есть сомнения в том, какой тип для здания нужно указать, то лучше использовать тип Здание без конкретного типа.

Итак, первый этап пройден, дом нарисован. Теперь хорошо бы нарисовать окружающую инфраструктуру и уточнить адресную информацию, добавив подъезды. Они обозначаются дополнительными точками на полигоне здания с указанием типа узла Вход/выход и типом входа staircase (за таким входом обычно скрывается лестница). Номер подъезда указывается в поле Номер/идентификатор. Если необходимо указать вход в частный дом, то значение типа входа должно быть home.


Как определить, где рисовать дополнительную точку входа в дом? Довольно часто для жилых домов можно определить количество и расположение подъездов по спутниковым снимкам: на крышах зданий видны лифтовые шахты и выходы вентиляционных труб. Однако, следует помнить, в большинстве случаев, особенно для высотных домов, крыша здания смещена относительно реального положения подъезда, поэтому необходимо также ориентироваться и по другим, косвенным признакам козырькам подъездов, входным дорожкам.


Так, на рисунке выше видно, что дома 14 и 18 имеют два подъезда, а в доме 16А их не менее пяти.

При рисовании объектов дополнительной инфраструктуры следует помнить, что корректность некоторых объектов зависит от направления их оцифровки. Например, реки оцифровываются от истока к устью, то есть в направлении течения; односторонние дороги должны быть оцифрованы по направлению движения. Морская береговая линия наносится так, чтобы при оцифровке вода была справа по ходу линии, а суша по левую. Крупные овраги, обрывы, насыпи оцифровываются так, чтобы понижение высоты рельефа находилось справа по ходу линии. Если объект был оцифрован в неверном направлении, то необходимо скорректировать его кнопкой Изменить направление.

Когда с оцифровкой закончено, наступает время отправить свои правки на сервер OpenStreetMap. Для этого в верхнем правом углу экрана есть кнопка Сохранить.


При сохранении пакета правок необходимо оставить комментарий, описывающий сделанные изменения. Нажимаем кнопку Сохранить и наслаждаемся своими изменениями на карте OSM.

В заключение


Не стоит бояться вносить свой вклад в проект OpenStreetMap, вместе мы сделаем его лучше и подробней. Однако подходить к редактированию карты нужно с умом: не разобравшись в тегах, нарисовав участок карты вразрез с правилами OSM и договоренностями сообщества, можно получить откат правок или гневные комментарии от других осмеров. Но не стоит сразу забиваться в свою ракушку: отвечайте на комментарии, признавайте свою вину или культурно (!) обосновывайте свою точку зрения. Ведь в споре рождается истина.
Подробнее..

День открытых данных 2021. Онлайн

01.03.2021 20:22:05 | Автор: admin

image


1-6 марта приглашаем на мероприятия, приуроченные к Международному Дню открытых данных 2021.


Это крупнейшее ежегодное международное событие, которое помогает продвигать концепцию открытых данных среди органов государственной власти, бизнес-корпораций, некоммерческих организаций и гражданского общества. Организатором Дня открытых данных в России выступает АНО Информационная культура.


Рассказываем, какие мероприятия мы приготовили для участников в этом году.
Накануне Дня открытых данных, с 1 по 5 марта, проведем серию практических онлайн мастер-классов по работе с открытыми данными.


  • 1 марта, мастер-класс Вскрываем декларации. Как при помощи регулярных выражений привести Wordовскую табличку к пригодной для анализа форме. Доступна видеозапись.
  • 2 марта, мастер-класс О чем говорят депутаты Госдумы? Анализ текстовых данных на Python.
  • 3 марта, мастер-классы по работе с геопространственными данными и картами для новичков и профи.
  • 4 марта, мастер-класс по поиску открытых данных от DataMasters.
  • 5 марта, мастер-класс Российская официальная статистика: как сделать работу с данными удобнее, а данные понятнее?.
  • 5 марта, мастер-класс Визуализация данных в ObservableHQ.

6 марта пройдет онлайн-конференция День открытых данных.


В центре внимания вопросы о том, что происходит с открытостью в России и мире и как использовать данные для эффективного решения конкретных проблем и задач общества. В дискуссиях примут участие не только российские эксперты, но и представители крупнейших международных проектов, продвигающих ценности и идеологию открытых данных: Global Data Barometer, The Humanitarian Data Exchange.


В программе дискуссии и выступления:


  • Дискуссия. Бизнес на открытости: зачем заниматься открытым кодом и открытыми данными
  • Дискуссия. Как инструменты оценки влияют на открытость государства?
  • Дискуссия. Доступность данных о госфинансах
  • Дискуссия. Данные переписи населения 2021: приватность vs. польза для общества
  • Выступления. Что происходит с тематикой открытости в мире?

Программа и регистрация: opendataday.ru/msk. Трансляция будет доступна и бесплатна для всех желающих.


Подробнее..

Как использовать GraphHopper для построения пешеходных маршрутов по собственным правилам

06.03.2021 00:17:06 | Автор: admin

Построение маршрутов..., люди регулярно этим пользуются, особенно для автомобильных маршрутов, в навигаторах.

Решений, для построения маршрута тоже немало, в том числе существует GraphHopper, который умеет строить маршруты, и для автомобилей, и для пешеходов, и даже для пешего туризма, - подойдёт, наверно, в 99% случаев.

Далее речь пойдёт том, что делать в остальных ситуациях, точнее о моём опыте использования GraphHopper, когда существующее решение не подходило. Требовалось учитывать дополнительные ограничения: строить пешеходные маршруты для людей с ограниченными возможностями. Не будет ни каких значимых особенностей реализации именно этой задачи. Обобщённо.

Будет описано, как создать на основе библиотеки GraphHopper свой вебсервис, который, по координатам начала и окончания пути, вернёт массив координат маршрута.

Пример приложения, со всеми необходимыми для запуска заглушками, можно найти в моём репозитории на GitHub.

GraphHopper - механизм маршрутизации, написанный на Java. Выпущен под лицензией Apache, и может быть встроен в продукты с закрытым исходным кодом.

Статьи подобного толка на хабре встречаются, например, Гуляем по городу с умом, но в ней не приводится деталей реализации, к сожалению, и ну и всё.

Также в публикации Новости из мира OpenStreetMap 512 (05.05.2020-11.05.2020), была новость следующего содержания:

Разработчики GraphHopper ждут наших с вами комментариев, так как они ввели новую функцию, позволяющую даже людям без знания программирования или Java изменять модель построения маршрутов.

Наверно, эта новая функция покроет ещё 0.99% возможных ситуаций, вероятно подойдёт и для Вашей задачи, знания Java не потребуются, и вообще проблем не возникнет. Я расскажу, а своём опыте создания правил построения маршрутов, когда этой функции не было, а до её создания оставалось 2 года.

Понадобятся знания Java.

Считаю, что публикация всё ещё актуальна, ибо:

  • ничто не может сравниться по гибкости и податливости с возможностью изменения исходного кода

  • GraphHopper работает на данных OSM, а Вам могут потребоваться правила, не предусмотренные OSM. Например, вы можете строить маршруты по закрытым дорогам, их закрытость очевидна из OSM. Вот только надо учесть цветовую дифференциации штанов. А ездить по зимникам в летнее время года я крайне не рекомендую, здесь может потребоваться проверка даты.

Решение

В статье используется версия библиотеки GraphHopper 0.10.0, актуальная на момент создания приложения.

Для начала подключаем библиотеку.

Maven:

<dependency><groupId>com.graphhopper</groupId><artifactId>graphhopper-reader-osm</artifactId><version>0.10.0</version></dependency> 

Исходный код GraphHopper, в том числе этой библиотеки, выложен на github. Так же там есть некоторая документация, например How to create new routing profile aka a new FlagEncoder? которая, как бы намекает, что нам необходимо создавать совой FlagEncoder. Уже существующие FlagEncoder, находятся в пакете com.graphhopper.routing.util, нас особо интересуют FootFlagEncoder, т.к. он занимается построением именно пешеходных маршрутов, и AbstractFlagEncoder, как его родительский класс.

Отправной точкой для постижения GraphHopper (актуальной версии) может стать вот эта страница GraphHopper Documentation и пример RoutingExample.java.

Создаём FlagEncoder

Имеет смысл, либо унаследовать свой FlagEncoder от AbstractFlagEncoder, частично повторив FootFlagEncoder и внеся изменения куда следует, либо сразу от FootFlagEncoder, что избавит от дублирования кода. Мне больше подходит наследование от AbstractFlagEncoder и копирование кода FootFlagEncoder, ибо требуется доступ к полям, которые в FootFlagEncoder приватны.

Магия построения графа путей сосредоточена в методе acceptWay, который принимает поочерёдно объекты дорог - ReaderWay и решает пригодна эта дорога для прохода/проезда или нет. Определение пригодности это уже прерогатива FlagEncoder. Я передаю во FlagEncoder список дорог, по которым ходить нельзя. Необходимо чтобы метод acceptWay, натолкнувшись на эту дорогу сказал своё твёрдое нет вернув 0.

Список назовём restricted, и хранить он будет id объекта way из OSM.

public class MyFlagEncoder {private List<Long> restricted;@Overridepublic long acceptWay(ReaderWay way) {        if (restricted.contains(way.getId()))            return 0;        }}

У нас запретительный подход, если объект оказался в списке, то выполнение прерываем, вернув 0.

Предварительная подготовка данных

Написав FlagEncoder, и переделав в нём всё что хотели, можно приступать к построению графа маршрутов.

Я черпал вдохновение в документации Routing via Java API.

GraphHopper closableInstance = new GraphHopperOSM().setOSMFile(osmFilePath).forServer();closableInstance.setStoreOnFlush(true);closableInstance.setGraphHopperLocation(graphFolder);closableInstance.setEncodingManager(new EncodingManager(encoder));closableInstance.setCHEnabled(false);GraphHopper hopper = closableInstance.importOrLoad();

Здесь

  • osmFilePath - путь к pbf-файлу региона, pbf можно взять например на geofabrik, или других порталов, это срез данных из OSM;

  • encoder объект интересующего нас FlagEncoder, например того, который мы сами и создали на предыдущем шаге;

  • graphFolder директория куда будут сохранены результаты построения.

Метод importOrLoad проведёт построение графа, в соответствии с правилами из FlagEncoder, и сохранит результат в указанную папку.

Строим маршрут

Нужно обратиться к следующей части документации GraphHopper: Low level API.

Предварительно созданные графы можно загрузить всё тем же методом importOrLoad.

GraphHopper closableInstance = new GraphHopperOSM().setOSMFile(pbfFile).forServer().setStoreOnFlush(true).setGraphHopperLocation(graphFolder).setEncodingManager(new EncodingManager(encoder)).setCHEnabled(false);GraphHopper hopper = closableInstance.importOrLoad();

Затем создать объект класса LocationIndex:

GraphHopperStorage graph = hopper.getGraphHopperStorage();LocationIndex index = new LocationIndexTree(graph, new RAMDirectory());index.prepareIndex();

Для построения маршрута нам потребуются объекты трёх классов: GraphHopperStorage, FlagEncoder, LocationIndex.

Используем их следующим образом, результатом будет List<Double[]>:

QueryResult fromQR = index.findClosest(fromLon, fromLat, EdgeFilter.ALL_EDGES);QueryResult toQR = index.findClosest(toLon, toLat, EdgeFilter.ALL_EDGES);QueryGraph queryGraph = new QueryGraph(graph);// Получить координаты путиqueryGraph.lookup(fromQR, toQR);Dijkstra dij = new Dijkstra(queryGraph, new FastestWeighting(encoder), TraversalMode.NODE_BASED);Path path = dij.calcPath(fromQR.getClosestNode(), toQR.getClosestNode());PointList pl = path.calcPoints();return pl.toGeoJson();

Заключение

Реализация получилась примитивной т.к. основана на проверке (в методе acceptWay) попадания объекта в заранее составленный (или полученный всеми правдами и неправдами) список:

if (restricted.contains(way.getId()))return 0;

Гораздо правильнее было сделать что-то подобное коду, основанному на проверке значений тегов из OSM, как здесь:

if (way.hasTag("foot", intendedValues)) {return acceptBit;}

Если у Вас есть возможность, для своей задачи, использовать второй вариант, основанный на проверке тегов лучше предпочесть его. Это ни как не помешает подмешать туда и дополнительную логику, не вписывающуюся в этот подход.

Удачи!

Подробнее..

Использование данных OSM для анализа

25.04.2021 10:05:42 | Автор: admin

Постановка задачи

В рамках проекта Фото-Географического Атласа России (photogeomap.ru) мы собрали ряд фотографий различных ландшафтов страны. Многие из них сделаны в достаточно труднодоступных местах. Именно эту труднодоступность на качественном уровне мы и хотим оценить для каждой точки (фотографии)/

Индекс недоступности

Характеристика, позволяющая косвенно, на качественном уровне оценить сложность попасть к точку съемки данного кадра. Поскольку не нашлось готовых решений по расчету такой характеристики. Решено было создать свою. Назовем ее индекс недоступности (ИН).

От чего он зависит? Очевидно, что от:

  • удаленности от дорог доступных для транспорта

  • удаленности от пеших троп и дорог пригодных только для пешего передвижения

  • удаленности от водных путей сообщения

Источник данных для анализа

Единственным доступным источником векторных данных для такого анализа близости нам видится OSM.

Спорные моменты и допущения

Сразу опишу все допущения принятые нами

  1. Все расчеты проведены с использованием данных OSM со всеми содержащимися в них огрехами и неточностями.
    На карте могут быть отображены НЕ ВСЕ тропы и не все дороги. Степень валидности существующих объектов также не обсуждалась.

  2. Все расчеты расстояний проведены не в лучшей для этого проекции но с учетом что нас интересуют довольно небольшие расстояния искажениями пренебрежём. Тем более что в финале все количественные характеристики все равно переводятся в качественные.

  3. При анализе близости от водных объектов не учитывалась их связность и судоходность. Что в общем случае конечно неверно, но мы сознательно вводим это допущение.

Подготовка данных OSM

1. Данные OSM на территорию РФ, полученные через https://download.geofabrik.de загружены в СУБД Postgres (c ext. PostGIS).
Основные нужные нам для анализа объекты расположены в таблице planet_osm_line.
Не забываем индексировать нужные нам поля (в случае дорог это поле highway)

2. Готовим дороги и тропы. Созданы materialize view для автодороги и тропинок из класса planet_osm_line.

Дороги - select * from planet_osm_line where highway is not null and highway != track (выбраны все типы дорог из данных OSM вне зависимости от типа и качества покрытия) ошибки неверного назначения тегов проигнорированы..

Тропы - select * from planet_osm_line where highway is not null and highway = track (выбраны тропинки)

На полученных m.view - тоже создаем индексы на нужное поле. Работать будет легче.

3. Готовим реки. Создаем materialize view для линейных рек и площадных водных объектов
Теги по которым можно выбрать реки смотрим ТУТ

Краткий анализ что у нас есть по рекам вообще -

--------------------------------
SELECT t.waterway , count (t.waterway) as cnt FROM public.osm_rivers_l as t where t.waterway is not null group by t.waterway order by cnt desc
---------------------------------

Реки (линейные)
select * from planet_osm_line where waterway is not null

Реки (площадные)
select * from planet_osm_polygon where water is not null

Расчет удаления от точек съемки

На этом этапе мы собственно считаем расстояния от наших точек (фото) до дорог, троп и рек.
Выполнить эту процедуру можно в любом настольном ГИС приложении , например в QGIS . В принципе, такой расчет можно провести в самом PostGIS, не вылезая из БД. Но я не программист и мне лень изучать и делать с нуля то, что я могу быстро за 10 мин сделать в той среде где работаю ежедневно (в GIS)

Определение расстояний от точек съемки до дорог - пишем в поле Road_dist и троп - Track_dist считаем все сразу в километрах! Определяем расстояние от линейных и площадных рек. Берем минимальное из пары (ближайший водные объект, неважно какой геометрии) и пишем в поле River_dist

Итак у нас есть поля с записанными в них расточениями. Собственно на этом использование данных OSM завершается, но не весь процесс..

Методика расчета

Теперь у нас все готово, и мы начинаем считать сам ИН.
Сначала мы переводим количественные показатели в качественные характеристики.

1. Введены градации расстояний (поля Road_cat и Track_cat) и присвоены значения весового коэффициента для удаленности от автодорог и троп (Road_cst и Track_cst)

Track_cst считается только для объектов с удаление от дорог более 5 км иначе принимается 0

до 1

до 0,5 часа пешком

1

от 1 до 5

до 2х часа

2

от 5 до 10

до 3х часов

4

от 10 до 25

до дня ходьбы

6

более 25

более 1 дня

10

2. Введены градации расстояний (River_cat) и присвоены значения весового коэффициента для удаленности от рек (River_cst) для объектов с удаление более 10 км от любых дорог и троп , иначе принимается 0

до 1 км

до 0,5 часа пешком

2

от 1до 5

до 2х часа

4

от 5 до 10

до 3х часов

6

более 10

до дня ходьбы

10

3. Вводим характеристику отражающую доступ только с акватории северных морей.
Поле Sea_cst весовой коэффициент по доступу с северных морей для объектов, расположенных в Арктике и на побережье северных морей (выбраны по карте визуальное их не много). В основном все объекты приняты за 0, кроме пары десятков.

с побережья северных морей

5

арктические острова

10

4. Все 4 поля *_cst суммированы в INDEX_IMP
смысл у него примерно такой - чем он выше --тем тяжелее добраться к точке.

менее 3 - относительно легко доступная точка (можно ии приехать на машине или относительно недалеко прийти пешком

в районое первого десятка приехать на машине и хорошо прогуляться.

второй десяток уровень автономной экспедиции.

более 30 очень сложно доступная точка - только с морских судов и пр пр радости

Собственно - все. Мы посчитали искомую характеристику и теперь можем расклассифицировать точки на карте по ней (для наглядности) или просто указать ее в атрибутах.

Подробнее..

Из песочницы Кому на Руси жить хорошо? Как мы искали самый зеленый город с помощью OpenStreetMap и Overpass API

24.09.2020 16:22:36 | Автор: admin
У каждой карты есть легенда: именно она говорит читателю о том, что и каким образом на ней обозначено. Домам соответствуют многоугольники серого цвета, дорогам отрезки и кривые, территории парков и скверов заливаются светло-зеленым и украшаются пиктограммой дерева. А к некоторым картам добавляют полезное приложение список этих самых дорог с протяженностью, количество парков, лавочек и мусорных урн.

Когда мы готовились к автоматизации работы местных муниципальных служб, мы обратили внимание на использование картографии, которое мы сочли действительно мощным и эффективным инструментом для решения широкого кластера задач. На карте можно отображать актуальную информацию о местоположении объектов, маршруты движения уборочной техники, транспорта, план работы и тепловую карту с проблемными и здоровыми участками.

Использование картографии в целом помогает ускорить время реакции и принятия решений по экстренным вопросам, упрощает процесс планирования и прогнозирования, а главное вся информация доступна в максимально наглядном виде.

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

Найти


Определить самый зеленый город в ЦФО по совокупности следующих данных: количество лавочек, урн и парков.

Дано


Картография. В качестве плацдарма мы выбрали OpenStreetMap (далее OSM) по весьма простой причине Open Source.

Решение


Существует несколько подходов к решению поставленной задачи. К примеру, мы можем использовать PostgreSQL для осуществление запросов к базе данных OSM и получения необходимых данных, или использовать QGIS систему для создания, анализа и публикации геопространственной информации. Однако, мы нашли более элегантный и эффективный способ Overpass API.

Overpass API мощный инструмент для извлечения данных из базы OSM по запросу пользователя. Он оптимизирован для задач любого масштаба: от получения нескольких элементов из базы до сотен миллионов объектов, которые отбираются согласно запросу в виде XML или Overpass QL модернизированной версии Overpass XML. Подробнее об Overpass API здесь.
Будем начинать все с чистого листа: в первую очередь потребуется развернуть OSM на сервере. В качестве операционной системы на нашей машине используется Ubuntu.

Q: Зачем разворачивать свой OSM?
A: Для работы с данными нам потребуется Overpass API, общедоступный сервер которого имеет ограничение на количество запросов в сутки. Именно по этой причине мы развернем свой сервер с блекджеком OSM и Overpass API.
Подробная инструкция по установке OSM лежит здесь.
Следующим шагом мы устанавливаем Overpass API его последний релиз можно найти тут.

sudo apt-get updatesudo apt-get install g++ make expat libexpat1-dev zlib1g-dev apache2 -ywget http://dev.overpass-api.de/releases/osm-3s_ВЕРСИЯ.tar.gztar -zxvf osm-3s_ВЕРСИЯ.tar.gzcd osm-3s_ВЕРСИЯ./configure CXXFLAGS="-O2" --prefix=$EXEC_DIRmake installcd ../chmod -R 755 ./overpass

После установки потребуется провести populating создание базы данных. База данных всего мира нам не нужна, поэтому мы воспользуемся сервисом Geofabrik, который позволяет получить данные по заданным административным регионам.
Достойная альтернатива Geofabrik BBBike.

Мы загружаем и распаковываем базу данных, загруженную из каталога Europe > Russian Federation > Central Federal District в формате .osm.bz2, по следующей схеме:
ПУТЬ_К_INIT_OSM3S.SH ПУТЬ_К_ФАЙЛУ_BZ2 ПУТЬ_КУДА_РАСПАКОВВАТЬ ДИРЕКТОРИЯ_OVERPASS_API
Примечание: в данном случае все действия выполняются в каталоге overpass.

Итак, платформа практически готова сейчас мы можем сделать свой первый запрос и узнать количество парков в целом по региону.
ПУТЬ_К_OSM3S_QUERY --db-dir = ПУТЬ_К_DB

Наш запрос и ответ выглядят следующим образом:

root@MIP-USER55:~# cd overpass/root@MIP-USER55:~/overpass# ./osm-3s_v0.7.56.7/bin/osm3s_query --db-dir=dbencoding remark: Please enter your query and terminate it with CTRL+D.[out:json][timeout:25];(nwr["landuse"="forest"];);out count;{  ...,  "elements": [{  "type": "count",  "id": 0,  "tags": {    "nodes": "23",    "ways": "19723",    "relations": "4206",    "total": "23952"  }}  ]}

Мы уже близко: осталось только разбить парки по областям и городам. Справиться с этой задачей нам поможет отдельно запущенный и постоянно работающий Dispatcher.

Запускаем API и в директории /etc/apache2/ports.conf добавляем Listen ПОРТ.

echo "ИМЯ_СЕРВЕРА localhost" | sudo tee /etc/apache2/conf-available/ИМЯ_СЕРВЕРА.conf && sudo a2enconf ИМЯ_СЕРВЕРАsudo iptables -A INPUT -m state --state NEW -m tcp -p tcp --dport ПОРТ -j ACCEPTsudo a2enmod cgisudo a2enmod ext_filtersudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/ИМЯ_КОНФИГА_ДЛЯ_СЕРВЕРА.conf

Проверяем конфигурацию. Она должна выглядеть следующим образом.

<VirtualHost *:ПОРТ>   ServerAdmin webmaster@localhost   ExtFilterDefine gzip mode=output cmd=/bin/gzip   DocumentRoot ПУТЬ_К_OVERPASS_API/html   ScriptAlias /api/ ПУТЬ_К_OVERPASS_API/cgi-bin/   <Directory "ПУТЬ_К_OVERPASS_API">      AllowOverride None      Options Indexes FollowSymLinks      Require all granted   </Directory>   <Directory "ПУТЬ_К_OVERPASS_API/cgi-bin/">      AllowOverride None      Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch      Require all granted   </Directory>   ErrorLog ${APACHE_LOG_DIR}/error.log   CustomLog ${APACHE_LOG_DIR}/access.log combined</VirtualHost>

Далее включаем новый хост и отправляем старый отдыхать.

sudo a2ensite КОНФИГ.confsudo a2dissite 000-default.confsudo a2dissite ВСЕ_ДРУГИЕ_КОНФИГИ.confsudo service apache2 reload

Настало время включить первый Dispatcher, который будет обрабатывать наши запросы. Для этого воспользуемся командой.

nohup ПУТЬ_К_OVERPASS_API/bin/dispatcher --osm-base --db-dir=ПУТЬ_К_DB --meta &

Первый признак того, что все пошло по плану создание файла
osm3s_OVERPASS_API_ВЕРСИЯ_osm_base в директории с базой данных. В файле nohup.out появится сообщение о статусе Dispatcher dispatcher just started.

Однако, работа на этом не заканчивается: требуется запустить ещё один Dispatcher. Для начала копируем папку rules в директорию с базой данных и раздаем права на появившийся в результате файл osm3s_OVERPASS_API_ВЕРСИЯ_areas.

nohup ПУТЬ_К_OVERPASS_API/bin/dispatcher --areas --db-dir=ПУТЬ_К_DB &chmod 666 "../db/osm3s_OVERPASS_API_ВЕРСИЯ_areas"nohup ПУТЬ_К_OVERPASS_API/bin/rules_loop.sh ПУТЬ_К_DB &

Теперь мы можем сделать запрос по области и подсчитать количество парков в Рязани. И, кстати, это уже работает в адресной строке браузера.

http://localhost:ПОРТ/api/interpreter?data=[output:json][timeout:25]; area[name="Рязань"]->.searchArea; ( nwr["leisure"="park"](area.searchArea); ); out count;

Долгожданный ответ!

"elements": [{  "type": "count",  "id": 0,  "tags": {    "nodes": "0",    "ways": "57",    "relations": "11",    "areas": "0",    "total": "68"  }}  ]}

Итак, теперь мы можем погрузиться в аналитику и вопросы благоустройства: найдем количество парков, лавочек и мусорных баков в 25 крупных городах ЦФО и сравним эти показатели при помощи аналитических инструментов платформы ODANT.
ODANT цифровая интеграционная платформа отечественной разработки, предназначенная для построения информационных систем различной сложности и распределенности. Подробнее об ODANT здесь.
Так выглядят наши параметры для поиска урн: в рамках задачи мы не делаем различий между контейнерными площадками, отдельно стоящими урнами и пунктами приема ТБО.

nwr["amenity"="recycling"](area.searchArea);nwr["amenity"="waste_disposal"](area.searchArea);nwr["amenity"="waste_basket"](area.searchArea);

Выходные данные мы экспортировали из ODANT в виде плоской таблицы.

Город


Урны


Население


Население/Урны


Владимир


1525


356937


234


Красногорск


274


175554


641


Калуга


376


332039


883


Тамбов


279


292140


1047


Москва


11473


12678079


1105


Коломна


111


140129


1262


Балашиха


378


507366


1342


Курск


336


452976


1348


Рыбинск


129


184635


1431


Мытищи


140


235504


1682


Люберцы


119


205295


1725


Зеленоград


140


250453


1789


Воронеж


544


1058261


1945


Тверь


225


449507


1998


Ярославль


273


608353


2228


Муром


43


106984


2488


Подольск


114


308130


2703


Химки


89


259550


2916


Кострома


72


276929


3846


Липецк


124


509420


4108


Рязань


118


539290


4570


Королёв


49


224348


4579


Тула


101


475161


4705


Электросталь


32


156026


4876




В данном рейтинге город Владимир занимает почетную первую строчку: на одно место скопления мусора там приходится всего 234 человека. Используя встроенные инструменты ODANT строим два занятных графика.





Перейдем к следующему элементу лавочкам.

nwr["amenity"="bench"](area.searchArea);

Получаем следующий результат в виде таблицы.

Город


Лавочки


Население


Население/лавочки


Владимир


593


356937


602


Тамбов


413


292140


707


Москва


13970


12678079


908


Красногорск


143


175554


1228


Балашиха


411


507366


1234


Зеленоград


185


250453


1354


Тверь


268


449507


1677


Люберцы


115


205295


1785


Калуга


176


332039


1887


Курск


214


452976


2117


Муром


42


106984


2547


Тула


172


475161


2763


Коломна


49


140129


2860


Воронеж


272


848752


3120


Подольск


79


308130


3900


Рязань


132


539290


4086


Кострома


61


276929


4540


Химки


43


259550


6036


Рыбинск


25


184635


7385


Ярославль


78


608353


7799


Липецк


55


509420


9262


Электросталь


16


156026


9752


Королёв


23


224348


9754


Мытищи


17


235504


13853



И снова Владимир оказался самым благоустроенным из всех претендентов: 602 человека на одну скамейку посадить не получится, но в Мытищах за места идет куда более ожесточенная борьба. Однако, всегда можно придумать расписание.

Визуализируем наши результаты.





Перейдем к самой интересной части: подсчет площади парков. Для решения этой задачи мы можем использовать несколько методов, но самым оупенсорсным и замечательным мы находим библиотеку Leaflet. В самом запросе потребуется заменить out count на out geom, что в результате даст нам координаты областей.
Leaflet библиотека с открытым исходным кодом, написанная на JavaScript, предназначенная для отображения карт на веб-сайтах. Поддерживает большинство мобильных и стационарных платформ из числа тех, что поддерживают HTML5 и CSS3. Leaflet позволяет разработчику, не знакомому с ГИС, легко отображать растровые карты, состоящие из маленьких фрагментов тайлов, с, возможно, дополнительными слоями, накладываемыми поверх основного. Подробнее о Leaflet здесь.

Создадим полигон.

const polygon = L.polygon(КООРДИНАТ).addTo(map);const area = L.GeometryUtil.geodesicArea(polygon.getLatLngs());

Теперь в area лежит площадь региона в м2. Извлекаем данные для выбранных нами городов и получаем следующую таблицу.

Город


Общая площадь парков (km2)


Площадь города (km2)


Процент пространства, занимаемого парками


Кол-во

парков


Зеленоград


4.13


37.199


0.111


26


Калуга


12.60


168.8


0.075


140


Москва


121.75


2561.5


0.048


1469


Люберцы


0.60


12.87


0.047


28


Владимир


4.62


137.14


0.034


104


Химки


3.55


109.8


0.032


27


Подольск


1.29


40.39


0.032


53


Балашиха


1.78


62.8


0.028


55


Тамбов


2.71


96.58


0.028


140


Тула


4.09


145.8


0.028


102


Кострома


3.95


144.5


0.027


50


Красногорск


0.69


25.65


0.027


21


Рязань


4.32


224.163


0.019


96


Ярославль


3.68


205.8


0.018


176


Королёв


0.97


55.47


0.017


30


Курск


3.31


208.2


0.016


862


Коломна


1.03


65.1


0.016


21


Мытищи


0.53


34.59


0.015


30


Воронеж


8.25


596.51


0.014


414


Липецк


4.44


330.15


0.013


78


Тверь


1.38


152.22


0.009


129


Муром


0.33


43.78


0.008


11


Рыбинск


0.65


101.42


0.006


62


Электросталь


0.25


51.45


0.005


52



По данным OSM Зеленоград полностью оправдывает свое название 11% площади города занято зелеными массивами. Получим отношение количества жителей к количеству парков.

Город


Общая площадь парков (м2)


Кол-во м2 парков на 1 жителя


Количество жителей на 1 парк


Калуга


12,600,000


37.95


2372


Зеленоград


4,130,000


16.49


9633


Кострома


3,950,000


14.26


5539


Химки


3,550,000


13.68


9613


Владимир


4,620,000


12.94


3432


Воронеж


8,250,000


9.72


2050


Москва


121,750,000


9.60


8630


Тамбов


2,710,000


9.28


2087


Липецк


4,440,000


8.72


6531


Тула


4,090,000


8.61


4658


Рязань


4,320,000


8.01


5618


Коломна


1,030,000


7.35


6673


Курск


3,310,000


7.31


525


Ярославль


3,680,000


6.05


3457


Королёв


970,000


4.32


7478


Подольск


1,290,000


4.19


5814


Красногорск


690,000


3.93


8360


Рыбинск


650,000


3.52


2978


Балашиха


1,780,000


3.51


9225


Муром


330,000


3.08


9726


Тверь


1,380,000


3.07


3485


Люберцы


600,000


2.92


7332


Мытищи


530,000


2.25


7850


Электросталь


250,000


1.60


3001



Представим данные в виде графиков.





И в финале рейтинг городов, подсчитанный по занимаемым местам.

Город


Рейтинг


Место


Владимир


12


1


Калуга


15


2


Москва


18


3


Зеленоград


21


4


Тамбов


23


5


Красногорск


35


6


Балашиха


39


7


Люберцы


45


8


Химки


46


9


Курск


47


10


Коломна


48


11


Кострома


50


12


Воронеж


52


13


Подольск


55


14


Тула


55


15


Рязань


61


16


Тверь


63


17


Ярославль


63


18


Муром


69


19


Рыбинск


69


20


Липецк


70


21


Королёв


75


22


Мытищи


75


23


Электросталь


94


24



Эпилог и выводы


Решением судейской коллегии в составе OpenStreetMap, Overpass API и ODANT,
г. Владимир занимает почетное первое место и получает титул Самый зеленый в ЦФО.

Стоит отметить следующий факт: наше исследование базировалось исключительно на данных, отраженных в OSM. Естественно, в Мытищах есть еще пара сотен скамеек, да и в Туле куда больше мест для скопления отходов. Наши результаты можно рассматривать с другой стороны, к примеру, для оценки динамики внесения объектов городской инфраструктуры в базу данных OSM. Однако, в среднем люди одинаково медленно вносят данные в OSM, поэтому мы можем считать, что данные сравнительно достоверны и заслуживают внимания.

Нам удалось решить поставленную задачу и найти эффективный способ работы с объектами на картах OSM: им оказалась комбинация Overpass API и Leaflet. С помощью данного набора инструментов мы можем делать запросы к БД OSM и получать данные о количестве объектов, вычислять площади областей. Для представления данных и аналитики мы собрали web-решение на базе ODANT, которое удовлетворило все наши потребности.

Существует большое количество отраслей производства, бизнеса и хозяйства, которым могло бы помочь данное решение. Если есть идеи или обратная связь было бы очень приятно обсудить. Мы потратили на задачу 28 чистых часов, возможно, у вас получится быстрее.

Ведущий Frontend-разработчик, Инфостандарт Епифанов Даниил.
Редактор, Инфостандарт Морозов Никита.
Подробнее..

Человейник, тебе меня не сломить! Анализ жилья в мегаполисе

03.02.2021 10:04:43 | Автор: admin
Вчера на них никто не обращал внимание, а сегодня они повсюду. Человейники атакуют! Официально они именуются ЖК эконом-класса, а по сути являются гигантскими бетонными коробками из дешевых материалов. Москва задает моду на уплотнительную застройку и возведение целых районов человейников. И вот уже из Питера, Новосибирска и других зон поражения раздается безысходное понастроили тут. Как всегда, под ударом простой народ. В красной зоне риска люди, созревшие для покупки или аренды квартир.

Что противопоставить диктату неконтроллируемой застройки? Нужен открытый и удобный инструмент для анализа качества городской среды, отдельно взятых районов и домов. Под прицелом не только плотность застройки, но и экология, уровень шума, доступность инфраструктуры и другие факторы, формирующие качество жизни в мегаполисе.


Человейники Гонконга. Интересно, можно ли будет сделать такую фотку в Москве лет через 30?

О проекте


ХоумХаб это молодой проект, развивающийся на энтузиазме и неравнодушии к будущему наших городов. Мегаполисы России повторяют путь агломераций Китая, сросшихся в один большой человейник с убитой экологией. Однако китайское правительство спохватилось, признало проблему и пытается выровнять ситуацию. В то время как Москва с маниакальным упорством обрастает микрорайонами со сверх-плотной застройкой сомнительного качества и отстутствием намеков на озеленение:


Типичный человейник заглядывает в окна кирпичного дома. Автор фотографии Михаил Мельников.

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

К слову об освещении подобных тем в СМИ: рождение NIMBY & BANANA
Английскими СМИ был сконструирован насмешливый и туповатый образ противника уплотнительной застройки. NIMBY (Not In My Back Yard) человек, который негодует, что у него под окнами строится ТЦ или на месте зеленого сквера возводится ЖК. В лучших традициях пропаганды образ был доведен до абсурда. Так родилась аббревиатура BANANA Build Absolutely Nothing Anywhere Near Anything: если абориген против точечной застройки в своем дворе, то наверняка и против любой застройки где бы то ни было, вот же фрик!

Цель проекта ХоумХаб - визуализация качества городской среды, подкрепленная объективными цифрами и понятными расчетами. Нам уже есть, что показать: на карте представлены данные по плотности застройки и экологической ситуации, аналогов которым в открытом доступе найти не удалось.

Мы в самом начале пути, наши модели не лишены упрощений и возможных ошибок, а многие фичи только предстоит реализовать. Пока мы охватили только Москву и ближайшую область. Зато работая с единственным городом, мы получаем кратчайший цикл пересборки карты, а на ранних этапах жизни проекта быстрая проверка гипотез нужна как воздух. Так выглядит карта ХоумХаба сейчас:


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

Из чего складывается качество жизни в мегаполисе


Насколько мегаполис комфортен для жизни и безопасен для здоровья, зависит от набора факторов городской среды. К большинству из этих факторов мы только примериваемся. Среди них:

  • Уровень шума.
  • Социальная инфраструктура.
  • Транспортная инфраструктура.

Зато у нас уже готов расклад по двум ключевым факторам:

  • Экология. Качество воздуха на наш взгляд важнейший аспект экологии, влияющий на жизнь в городе. Поэтому в разделе Экология сейчас представлены два слоя: качество воздуха и источники загрязнения, среди которых ТЭЦ, заводы и водоочистные сооружения.
  • Застройка. В этом разделе доступны два взаимодополняющих слоя: процент плотности застройки и процент застройки.

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

Плотность застройки


Плотность застройки характеризует интенсивность использования территории. Слишком плотная застройка таит ряд опасностей:

Огромная нагрузка на социальную и транспортную инфраструктуру. Несоразмерная концентрация жителей на клочке земли приводит к хайлоаду для близлежащих магазинов, парковок и дорог. А это очереди к лифтам, очереди за продуктами и пробки при подъезде к дому.


Хватит ли пропускной способности этой дороги, когда достроятся все 45 корпусов? Кстати, как на единственной полосе разъезжаться?

Нехватка солнечного света. Существует специальный ГОСТ о продолжительности инсоляции (естественного освещения) жилых помещений. Застройка окна-в-окна его игнорирует чуть более чем полностью:


Солнечные лучи попадают в этот двор и окна максимум на 40 минут в день. Фотография взята из прекрасного поста про питерский колодец-могильник.

Разрушение природной экосистемы. Сравните просторные зеленые дворы между кирпичными домами советской постройки и заставленные машинами бетонные каньоны новостроек. Березы или яблоневый садик под окнами? Забудьте.

Риски при возникновении ЧП. В случае пожара построенные впритык здания не дают проехать спасательной технике, а высокая этажность блокирует работу пожарных команд. Вишенка на многоэтажном торте: многие человейники в силу своей дешевизны попросту не оборудованы средствами пожаротушения.

Огромная плотность населения безупречные условия для распространения пандемии. COVID-19 непрозрачно намекнул, что ряды высоченных человейников с одним супермаркетом на весь микрорайон так себе идея.

А почему почти все человейники так пестро разукрашены? Вырвиглазные прямоугольники и полосы на стенах, корпуса контрастных цветов. Нет, не эстетики ради. Дело в том, что яркие вставки ломают визуальную целостность человейника. Как камуфляж. Грядки монолитных гигантских коробок давят на психику и угнетают гораздо мощнее, если они однотонные. Представьте, какое впечатление производили бы эти кварталы, будь они одноцветными:




Коробки в боевой раскраске. Обе фотографии взяты из блога фотографа, столкнувшегося с человейниками.

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

Справедливости ради стоит упомянуть и недостаток слишком разреженной застройки: подскакивают расходы на поддержание транспортной инфраструктуры и инженерных коммуникаций. Но как вы догадываетесь, Нерезиновой это не грозит.

Расчет плотности застройки


Плотность застройки города принято оценивать по двум параметрам:

Процент застройки процентное соотношение площади зданий к общей площади участка, на котором они стоят.

Процент плотности застройки процентное соотношение суммарной поэтажной площади зданий на участке к его площади.

В разных странах и городах исторически закрепились свои нюансы расчетов. Например, в Нью-Йорке площадь здания отмеряется от внутренних стен, а площадь участка берется строго по его границам. В Лондоне площадь здания меряется от наружных стен, а площадь участка захватывает половины улиц, окружающих участок. Во многих странах принято измерять плотность застройки в расчете на гектар земли (1 га = 10 000 м), а в наших мегаполисах прижилось измерение в расчете на квартал. Площади кварталов могут отличаться в разы, и это привносит неразбериху.

С точки зрения оценки качества жизни ни один из перечисленных способов расчетов нас не устраивает. Они ориентированы на градостроителей и архитекторов, а не на людей, населяющих дома. Эти способы слишком огрубленные: если участок представляет собой пустырь с супер-плотной застройкой по центру, озвученные методы выведут среднее по больнице, и это, согласитесь, не интересно. Также они не учитывают множества граничных случаев. Допустим, один из участков зажат между несколькими соседними, а другой стоит у парка. Эти способы не выявят разницу между их плотностью. Хотя понятно, что дом возле парка застроен менее плотно, чем дом, зажатый в кольцо соседей.

Мы предлагаем способ расчетов, заточенный не для задач застройщиков, а для адекватного выбора жилья. Он хорош тем, что основывается на вычислении застройки для каждого дома в отдельности. Для этого нужно определить геометрию участка вокруг дома; найти его площадь с учетом некоторых нюансов; найти площади стоящих на участке домов. Поехали!

Будем расчитывать процент плотности застройки и процент застройки вот для этого дома:


Москва, Ломоносовский проспект, 14.

Геометрию, этажность и другие атрибуты домов мы импортируем из OSM в табличку PostgreSQL, для которой включено расширение PostGIS. В нем есть масса удобных функций для работы с географическими и геометрическими объектами.

Итак, извлекаем геометрию интересующего дома из таблицы. Отступаем от нее 200 м во все стороны. Получившийся многоугольник и есть интересующий нас участок земли. Именно для такой небольшой площади мы будем считать процент плотности застройки и процент застройки.


Вид сверху. Контуры дома фиолетовые. Контуры участка, для которого мы будем расчитывать застройку синие. 17 строений, попавших на участок оранжевые.

Почему конутры участка на скриншоте получились такой странной формы? Они расчитаны с помощью функции PostGIS ST_Buffer(geometry, radius_of_buffer), формирующей полилинию точек, отстоящих от заданной геометрии на расстояние radius_of_buffer. Параметр radius_of_buffer равен 200 м, а сглаживание углов включено по умолчанию. Для наглядности вот примеры результатов ST_Buffer() на этом и других зданиях, но с radius_of_buffer всего в 10 м:


Контуры домов фиолетовые. Контуры участка, полученные с помощью ST_Buffer() синие.

Сформировав таким образом участок вокруг дома, находим и суммируем площади всех зданий на участке. Если здание попало на участок частично, мы учитываем только площадь пересечения. Таким образом мы получаем общую площадь зданий на участке.

Теперь находим все попадающие на участок водоемы. Вычитаем их площадь пересечения с участком из общей площади участка. Мотивация простая: требуется определить площадь участка вокруг дома, по которой можно спокойно перемещаться. Грубо говоря, у дома на скале, вокруг которого океан, застройка примерно 100%.

С этой же целью находим все трамвайные, монорельсовые и ЖД-пути, а также дороги, не являющиеся внутридворовыми проездами и попадающие на участок. Вычитаем их площади из площади участка. Чтобы посчитать площадь дороги, нужно найти ее ширину. Но как?

Для некоторых дорог в OSM указан тег lanes, обозначающий количество полос. Для обозначения значимости дороги используется тег highway. По тому, является ли дорога автомагистралью федерального значения, городской трассой или внутриквартальным проездом, можно предположить ее полосность. Ширину полосы определяет ГОСТ: она зависит от типа трассы и максимально допустимой скорости (OSM-тег maxspeed). Например, для highway=motorway ширина полосы может быть 3.75 м, а для дороги во дворе 2.75 м.


Дороги внутри участка отмечены темно-серым, трамвайные пути светло-серым.

На даннном этапе у нас уже посчитана общая площадь участка, которая годится для расчета параметров застройки. Для определения процента застройки берем процентное соотношение площади домов к площади участка. Оно может варьироваться от 0 до 100%. Имея 100-балльную шкалу, подбираем для нее цвета от насыщенно-зеленого для пустынных участков до темно-красного для максимально застроенных.

Процент застройки дома 17%:


Видно, что между домами есть дворы и широкие пешеходные зоны. Поэтому у дома 14, как и у соседних домов, невысокий процент застройки. На слое ХоумХаба процент застройки они отображаются светло-зеленым.

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

Процент плотности застройки дома 136%:


В OSM указано, что у интересующего дома 10 этажей. Со всех сторон он окружен другими зданиями и дорогами, поэтому в отличие от соседнего дома возле сквера он подсвечен более светлым оттенком зеленого на слое процент плотности застройки.

Процент плотности застройки на практике варьируется от нуля до тысяч процентов (и лишь физики ведают, до какого предела в теории). Здания с небольшим значением процента плотности отображаются на карте зеленым. Дома, для которых в OSM не отмечена этажность, отображаются фиолетовым. Но какое значение принять за максимальное для соотнесения ему темно-красного? Роль референса для дна досталась, кто бы сомневался, деловому району Москва-Сити с процентом плотности застройки, перевалившим за тысячу.


Приз в номинации Максимальный процент плотности застройки в Москве заслуженно получает Москва-Сити. Но почивать на лаврах не в правилах этого делового района: он активно пополняется новыми высотками.

С рекордсменом по проценту плотности застройки все понятно. А где самый высокий процент застройки? Конечно же, в центре города. Во многих дворах внутри Садового кольца он достигает 85%. Но для исторического центра это нормально: многие европейские города могут похвастать 95% застройки.


Приз в номинации Максимальный процент застройки в Москве вручается дворам возле Тверской улицы.

А вот как выглядит на карте уплотнительная застройка дворов возле метро Домодедовская:


Слева блочки с адекватной плотностью застройки, справа новый ЖК бизнес-класса, застроенный окна-в-окна. Ибо сказано: за слово бизнес в названии и входную группу с искуственным мрамором не стыдно доплатить по тарифу x2.

Процент застройки и процент плотности застройки отображаются для каждого дома в отдельности начиная с 13 уровня зума. На мелких масштабах уже требуется генерализация: обобщение значения таким образом, чтобы его было удобно анализировать для больших площадей на обзорных зумах.

Первая попытка генерализации провалилась. План был таков: при отдалении от 13 зума (ниже которого отдельные дома уже не видны) процент застройки обобщается для административных районов, и они подсвечиваются соответствующим цветом. Если продолжать отдалять карту, процент застройки обобщается для городских округов.

Результат нас разочаровал: даже внутри небольшого района, захватывающего несколько улиц, возможны сильные флуктуации застройки. Не спасут ни медиана, ни среднее взвешенное: любое усреднение по районам обречено на сглаживание. Пресловутый Москва-Сити окружен жилыми домами со скромным процентом плотности застройки, и обобщенный процент для Пресненского района не выявит в этом районе абсолютно никакой аномалии.

Поэтому мы реализовали более наглядный способ генерализации:

1) Замостить карту гексагонами с длиной ребра 100 метров для 12 уровня зума, 200 м для 11 зума и так далее вплоть до 6 зума. Для этого была использована функция PostGIS ST_HexagonGrid(hex_edge_len, bounds).

2) Найти дома, попадающие внутрь гексагона. Рассчитать среднее взвешенное процента застройки и процента плотности застройки по полученным значениям.

3) Залить гексагоны соответствующими цветами. В результате генерализация будет выглядеть примерно так:


Гексагональная генерализация процента застройки: средние уровни зума, на которых виден центр города.

Резюмируем: фактор застройка для оценки качества городской среды лучше всего анализировать в разрезе двух параметров: процента плотности застройки и процента застройки. Именно их комбинация позволит увидеть картину целиком.


Обратная сторона ХоумХаба


Настало время поговорить про технологии, которые стоят за нашей картой.

Наши источники данных


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

При подготовке слоев с застройкой мы столкнулись с проблемой: многие рорайоны Москвы появились недавно, оторваны от основной транспортной сети, и добираются туда в основном только местные жители. Как правило, по единственной замершей в вечной пробке дороге. Следовательно, такие места замаплены в OSM из рук вон плохо или вовсе отсутствуют на карте. Мы стараемся это исправить, но наши усилия капля в море всех человейников.

Для карты рельефа мы используем DEM (digital elevation model) SRTM v3. Это данные, полученные радарной топографической съемкой материковых и островных территорий с 54 южной широты по 60 северной широты.

SRTM считается одним из самых качественных открытых датасетов, и его точности (1 измерние высоты примерно на 50 метров поверхности) должно хватить для адекватного использования в моделях экологии и распространения шума.

Пайплайн генерации тайлов


Наша цепочка python-скриптов для скачивания данных, импорта в PostgreSQL, подсчета данных для слоев и нарезки их на тайлы разбита на несколько docker-образов и запускается через docker-compose. Весь процесс выглядит так:

Подготовка растровых тайлов с рельефом местности. Датасет SRTM состоит из набора растровых тайлов 1 x 1, которые мы пропускаем через последовательность GDAL-утилит для генерации прозрачных тайлов с хилшейдингом, тайлов с цветовым рельефом и совмещения этих двух наборов в комбинированные тайлы рельефа местности.

Подготовка векторных тайлов с данными из OSM, слоями застройки и экологии. С ресурса Geofabrik скачивается фрагмент OSM, а конкретно Центральный федеральный округ России. Из него с помощью osmium-extract извлекается окружность, включающая Москву и некоторую часть МО.

Для заливки данных в PostgreSQL запускается импортер Imposm3. Он хорош тем, что позволяет удобно сконфигурировать параметры генерализации для лесов, озер, дорог и произвольных объектов.

Далее в PostgreSQL создаются дополнительные столбцы, таблицы, и стартует скрипт подготовки данных для слоев с экологией и застройкой. Он-то и реализует логику расчета процента плотности застройки, процента застройки, качества воздуха и заполнения таблицы с источниками загрязнения.

Финальный аккорд запуск генерации векторных тайлов. Для этого используется самописный скрипт с конфигом, конкретизирующим, какие зумы и с какими данными генерировать. В PostGIS есть все необходимые для этого функции, такие как ST_MakeEnvelope(), ST_Intersects(), ST_AsMVT(), ST_AsMvtGeom().

Вот карта и готова. Для отрисовки в браузере используется опенсорсная библиотека Mapbox GL JS. Стиль карты был запилен по-простому, безо всяких WYSIWYG-примочек, в виде гигантского json в формате Mapbox.

О планах и о команде


Работа над проектом ХоумХаб началась пол года назад. Из моих туманных набросков и острого желания делать карту Irkest сформировал нынешнюю концепцию проекта. Также он накладывает вето на малейшую попытку прикрутить что-то красивое, но дурацкое.

Архитектор проекта, фулстек-разработчик, девопсер, дизайнер, веб-мастер и копирайтер-на-пол-ставки это все я. В душе я бэкендер, поэтому вместо мобильной версии сайта на js у меня получилась консольная тулза на C++. Пришлось переделывать. Вышел микросервис. Разработка забуксовала. К счастью, в проект ворвался Rostixman: он за одно воскресенье запилил вменяемую мобильную версию и пофиксил косяки интерфейса, за что ему отдельное спасибо.

ХоумХаб развивается, задач становится все больше и они все заковыристее. Поэтому наша команда упоротых исследователей мегаполиса ищет новых разработчиков, особенно фронтендеров! Будем рады разработчикам, которым близка идея проекта и которые готовы посвящать ему свое время.

Мы планируем добавить на карту новые важные факторы, влияющие на качество городской среды:

Уровень шума. По нашим наблюдениям это самый недооцененный параметр, выстреливающий в ногу при выборе жилья! У нас накопилось полно историй людей, не сталкивавшихся с проблемой шума ранее и поэтому не задумывавшихся о ней при покупке недвижимости. Они тратили свои средства на квартиру, в которой впоследствии не могли по-человечески спать и отдыхать. Мы готовимся построить модель распространения акустических волн с учетом разнообразных источников, препятствий и свойств местности.

Социальная инфраструктура: наличие в пешей доступности магазинов, школ, аптек и всякой всячины вплоть до бассейнов. К сожалению, для многих новых районов даже наличие поликлиники недоступная роскошь.

Транспортная инфраструктура. Боль многих городов-сателлитов в вечной пробке на единственной дороге между сателлитом и мегаполисом. А у многих микрорайонов проблемы с доступностью на общественном транспорте. Поэтому мы планируем сделать два слоя: для автомобилистов и для приверженцев общественного транспорта.

После того как большинство факторов качества городской среды будет реализовано, мы предоставим удобный интерфейс для выбора интересующих факторов и опциональной установки их важности. Можно будет подбирать подходящие под конкретные требования микрорайоны и дома. Кому-то очень важна экология, а транспортная доступность второстепенна. Кто-то в первую очередь обращает внимание на уровень социальной инфраструктуры и плотность застойки. В общем, можно будет находить жилье на любой вкус.

Кроме того, мы собираемся охватить как можно больше городов, в идеале всю Россию. Было бы интересно посравнивать наши мегаполисы с Гонконгом, Нью-Йорком и прочими знаменитыми рассадниками человейников. А исторический центр сравить например с центром Праги или Рима.

А что за карта без гео-поиска? Мы сделаем и его. Но конечно же приоритет 1 улучшать, валидировать и снова улучшать наши модели.

Заключение


ХоумХаб это карта со слоями, которые помогают находить ответы на наболевшие вопросы. Какая плотность застройки в новом микрорайоне? Обнаружены ли в нем зачатки инфраструктуры и можно ли из него выбраться своим ходом? Сколько заводов и промзон в Капотне? Стоит ли ценителю тишины покупать квартиру с окнами на Ленинградское шоссе? Как долго идти от ЖК Грин Парк до ближайшего парка?

А на какие вопросы ищете ответа вы? Делитесь мнением, реквестуйте фичи. Критикуйте, это же Хабр в конце концов! А мы в предстоящих релизах будем приоткрывать новые грани жизни в мегаполисе.


Под занавес: это не Гонконг, это Марфино. А вы бы хотели, чтобы наши мегаполисы превратились в сплошной человейник?
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru