0,0
рейтинг
16 ноября 2010 в 06:48

Разработка → OSM и карта лежачих полицейских в навигаторах

imageТак как начинать с начала неинтересно, начну с конца.
Мы ее-таки сделали. Достали из данных OpenStreetMap лежачих полицейских, скрестили их со страшной коммерческой программой Навител, сделали веб-просмотрщик этих самых лежачих полицейских, и интерфейс для их добавления для новичков на http://latlon.org/tc/. И даже написали небольшой пресс-релизик, ссылку на который можно разослать друзьям и знакомым-автомобилистам.
Но для хабра можно рассказать и кое-что особенное: как это всё устроено внутри, и как оно делалось.

Главное правило OpenStreetMap — Have fun. Всё описанное делалось не ради денег или ещё каких-то плюшек, и даже не для собственного удобства.

На одной из Минских мини-OSMовок (мини, потому что нигде, кроме IRC-канала они не объявляются) меня наградили заданием: «хочешь сделать полезное дело? достань из осма лежекопы в навител». Потом всё как-то забылось, и через пару месяцев таки вспомнилось, благодаря героическому труду товарища stas_symba c форума 4pda.ru. Он уже несколько месяцев в одиночку собирал и выкладывал обновления по лежачим полицейским по Беларуси. «Но ведь такие данные проще собирать сразу в OSM», — подумалось мне, и я засел за экспорт.

Формат


Так как ни навигатора, ни Навитела, ни автомобиля у меня нет, делать всё пришлось вслепую, читая форумы и думая мозгами. Для начала и понимания масштаба бедствия — вкратце про то, что удалось добыть про формат данных из форумов.

Файл cops.txt (и/или speedcam.txt) — обычный csv, в первой строчке — заголовок, во всех последующих — данные. Заголовок:

idx,x,y,type,speed,dirtype,direction
где:
  • idx — уникальный номер объекта;
  • x,y — координаты, в проекции долгота-широта EPSG:4326;
  • speed — разрешенная скорость для объекта;
  • dirtype — тип направления действия — в 1 или 2 стороны;
  • direction — нправление действия;


Выбор инструмента

Инструменты для решения задачи обычно выбираются из того, что есть под рукой. Под рукой было множество скриптов для поточной обработки OSM XML, и база PostGIS. Несмотря на то, что привычнее работать как раз с поточными скриптами, последнее поле файла спидкамов намекало, что придётся поработать с геометрией объектов, чем и славится PostGIS.

Для начала на домашнем сервере при помощи osm2pgsql была импортирована обычная база для Mapnik. Первый запрос написался быстро и просто:
select * from planet_osm_point where traffic_calming is not NULL;
В ответ на это мне вернулось множество полей, в том числе геометрия в формате WKB, закодированная в hex. Совсем не годится.

Пришлось залезть в мауал по PostGIS, в котором нашлись функции ST_X и ST_Y. Показалось, то, что надо. Переписываем:
gis=> select st_x(way), st_y(way) from planet_osm_point where traffic_calming is not NULL limit 1;
st_x | st_y
------------------+------------------
3085590.21426068 | 7159526.18035388
(1 запись)


Неожиданно? Ожиданно, но надо сделать пояснение.

В веб-картографии широко используются две проекции: для передачи и показа пользователю — широта-долгота, она же EPSG:4326, и «гугловская», она же «меркатор на сфере», она же EPSG:3857, она же EPSG:900913, она же EPSG:3785 (почему так много кодов? долгая история споров больших корпораций, любителей и регистраторов, достойная отдельного поста). Проекция хороша тем, что переход из нее в 4326 на любом языке занимает от силы две строчки математики. Именно она, метрическая, а не в угловых координатах, используется при выводе карт на экран. И для облегчения рассчётов, именно в ней хранится база Mapnik.

Хорошо, ладно. Перепроецируем.
gis=> select ST_X(transform(p.way,94326)) as X, ST_Y(transform(p.way,94326)) as Y from planet_osm_point p where traffic_calming is not NULL limit 1;
x | y
------------+------------
27.7183285 | 53.9438334
(1 запись)


Что там дальше? Скорость.

Никто в OSM на лежачие полицейские скорость не проставляет. Надо придумывать, откуда ее брать. Спрашиваем первого попавшегося человека (им оказался wildMan), получаем ответ — по ПДД разрешенная скорость 60 в городе, 90 вне города. А ещё может быть ограничение по скорости на самой дороге, где ее занесут в соответствющий тег — maxspeed. Ну, заодно можно из тега oneway достать признак односторонности — зря, что ли, в формате поле под него отведено?

Объединяем с таблицей полигонов — в ней хранятся административные границы, и таблицей линий — в ней хранятся линии дорог.

select ST_X(transform(p.way,94326)) as X,
ST_Y(transform(p.way,94326)) as Y, '102' as TYPE,
case when l.maxspeed is not NULL then l.maxspeed else case when t.admin_level = '8' then '60' else '90' end end as SPEED,
case when l.oneway = 'yes' then '1' else '2' end as DirType

from (planet_osm_point p
join planet_osm_line l on (l.highway is not NULL and ST_DWithin(p.way,l.way,.1)))
LEFT OUTER JOIN planet_osm_polygon t on (t.admin_level = '8' and ST_Within(p.way, t.way))
where p.traffic_calming is not NULL;


При помощи стандартного SQL join мы сопоставляем все три таблицы — каждому лежачему полицейскому находим в пару линию дороги в не дальше, чем десяти сантиметрах от нее, и полигон города, ее окружающий, если получится. Далее, поле speed мы пытаемся заполнить по тегу maxspeed линии, а если его не заполнили, то смотрим, попала ли точка в административную границу города: если да, ставим 60, если нет — 90.

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

Страх и отсутствие документации


Так как Навител — продукт коммерческий, и ни исходных текстов, ни нормальной демо-версии у него нет, осталась без комментариев последняя цифра — направление. Никаких намёков о том, как оно считается, найти не удалось, кроме того, что это явно — цифра в градусах.
«Ну, раз все карты в меркаторовской проекции, и она сохраняет углы и направления, то, значит, они посчитаны в ней!»

Чтобы посчитать азимут, PostGIS требует две точки. Очевидно, они должны лежать на той же самой дороге, на которой установлен лежачий полицейский, на некотором расстоянии в обе стороны от него.
Метод ST_Line_Locate_Point позволил найти длину вдоль линии от ее начала до заданной точки, а ST_Line_Interpolate_Point — наоборот, по длине вдоль линии найти точку.

Но psql рисует в ответ на запросы мне табличку в псевдографике, когда мне нужен CSV! Неужели писать обёртку?
Оказалось, что всё намного проще. Функция COPY позволяет доставить результат запроса сразу в CSV — как раз то, что нужно. Вот такая маленькая кошачья радость.

Выгоняю первый результат, нахожу обладателя Навитела, заставляю проверить… и упс. Направления не параллельны дорогам. Возникает мысль «неужели...», и лапы сам пишут перепроецирование в 4326 при расчёте азимута.

Да, доблестные разработчики Навитела считают направление в проекции, не сохраняющей направления. Честь им, хвала и почёт.

Квазиитог


В итоге, запросом стал вот такой вот монстрик, добывающий из данных OpenStreetMap то, чего в них изначально как будто бы и нет — лежачих полицейских в формате Навител. Запрос, достающий из них камеры контроля скорости, можете написать в качестве домашнего задания. :)
CREATE TEMP SEQUENCE idx ;
COPY (

select nextval('idx') as idx,
ST_X(transform(p.way,94326)) as X,
ST_Y(transform(p.way,94326)) as Y, '102' as TYPE,
case when l.maxspeed is not NULL then l.maxspeed else case when t.admin_level = '8' then '60' else '90' end end as SPEED,
case when l.oneway = 'yes' then '1' else '2' end as DirType,
floor(ST_Azimuth(
transform(ST_Line_Interpolate_Point(l.way,
case when (ST_Line_Locate_Point(l.way,p.way)-0.01 < 0) then 0 else ST_Line_Locate_Point(l.way,p.way)-0.0000001 end),94326),
transform(ST_Line_Interpolate_Point(l.way,
case when (ST_Line_Locate_Point(l.way,p.way)+0.01 > 1) then 1 else ST_Line_Locate_Point(l.way,p.way)+0.0000001 end)
,94326))/(2*pi())*360) as Direction
from (planet_osm_point p
join planet_osm_line l on (l.highway is not NULL and ST_DWithin(p.way,l.way,.1)))
LEFT OUTER JOIN planet_osm_polygon t on (t.admin_level = '8' and ST_Within(p.way, t.way))
where p.traffic_calming is not NULL
) TO STDOUT WITH CSV HEADER;


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

Ближе к выходным товарищ andrewsh (думаю, ему тоже есть о чём рассказать, если его попросить :) написал удобный интерфейс для сообщения о новых лежачих полицейских без регистрации — latlon.org/tc, и все сервисы было решено пустить в народ. Надеюсь, вам понравится.

Have fun! ;)
Дорофей Пролесковский @Komzpa
карма
80,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (32)

  • 0
    Пардон за возможно глупый вопрос, а как это все в навител попадает?
    • 0
      Файл cops.txt копируется во соответствующий каталог неподалёку от программы, и при следующем запуске, если всё пройдёт удачно, она его подхватит и покажет на экране, и озвучит голосовыми предупреждениями.
      • 0
        А иконки уже зашиты?.. Может было проще сделать на основе Яндекс.Карты?!
        • +1
          не у всех в пути есть интернеты
        • 0
          «На основе яндекс-карты» сделать что именно?

          Яндекс-карты не предоставляют просым смертным информацию о лежачих полицейских.

          Показывать данные из OSM поверх карты Яндекса, на мой взгляд, моветон. :)

          Ну и рендеринг всего этого чуда в png-тайлы является тем же самым sql-запросом + пара строчек стиля «повесить вот эту иконку» и «написать цифру циферкой в красной обводке». Выгонку в kml пришлось бы писать отдельно, руками.
  • +1
    > Никто в OSM на лежачие полицейские скорость не проставляет. Надо придумывать, откуда ее брать.
    Не очень понял про скорость. По идее на любом «полежае» скорость ограничене 20 км/ч (изредка 30 км/ч).
    • +1
      В Минске чаще 40 км/ч
      • +1
        тогда можно везде поставить 40
  • +1
    Скажите, пожалуйста, а зачем в навигаторе карта лежачих полицейских?
    • +5
      Разве не понятно — чтобы совсем не смотреть ни на знаки, ни на дорогу :)
      Ну а если серьезно, если поздно вечером едешь по незнакомому неосвещенному городу в дождь или снег, такая инфа может оказаться очень полезной.
      • 0
        если едешь по «незнакомому неосвещенному городу в дождь или снег», то тем более надо смотреть на дорогу, а не на иконки на навигаторе :)
        • +2
          навигатор может пищать или свистеть перед такого рода препятствием. Не обязательно в него смотреть вообще.

          Вообще я бы в такую карту еще особо крупные ямы вносил ;)
        • +3
          На полицаи не всегда ненесена соответствующая разметка.
          Ставить соответсвующие знаки часто забывают (например у нас в городе на одной улице штук 6 полицаев, дорожники чтобы не ставить все необходимые знаки, поставили знак прочие опасности и успокоились).
          Знаки иногда банально не видны (например на участке по которому я часто езжу есть полицай, который находится через 10 метров после начала спуска. Тоесть пока ты не начнешь спуск, ты его в принципе не увидишь. А за эти 10 метров ты особо скорость сбросить не успеешь)
        • +1
          У меня навигатор с ОСМ картой, подающий сигнал о лежащих полицейских.

          И как минимум пару-раз он меня уберег от неожиданного подпрыга на бетонном бугре замаскированным под цвет дороги, без разметки и знаков. %)) Не забывайте что лежекопы тут бывают очень разными. )

          Мало того, правильный маршрутопрокладчик должен учитывать потери скорости на лежекопах, и при наличии маршрута без них — прокладывать в обход.
      • –1
        Лежачие полицейские и так устанавливаются обычно в таких местах, где знаки и разметка предписывают снизить скорость и повысить бдительность, и главная функция навигатора в таком месте — предупредить о превышении скорости, а не о том, что через 100 метров расположен лежачий полицейский.
        • НЛО прилетело и опубликовало эту надпись здесь
  • +1
    Чтобы получать предупреждение о том, что по пути лежекоп.
  • +2
    А можно еще карту ям и пешеходных переходов? А то совсем неинтересно на дорогу смотреть :)
    • 0
      А для ям и пешеходных переходов есть тип у Навитела?
  • +1
    даешь карту светофоров!
    • +1
      + точное время их переключения на зеленый свет
      • 0
        Запросто.
        Машинно-читаемый тег http://wiki.openstreetmap.org/wiki/RU:Key:opening_hours применим к любым подобным периодически-расписанным вещам.
        Правда, у меня нет подобной информации, и в OSM ее никто пока тоже не вносил. Если она у вас есть, вы можете внести и попробовать сделать что-то подобное :)
    • 0
      Давайте. Светофоры наносятся как highway=traffic_signals.
      http://wiki.openstreetmap.org/wiki/RU:Tag:highway%3Dtraffic_signals
      Это открытый проект, достаточно подробное описание, как сделать выгрузку, написано выше. Хотите попробовать сами?)
  • 0
    А как можно для навитела выдернуть часть точек, например для Москвы и МО?
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      нет. для гармина будет отдельная генерация точек. в форматах *.gpx и возможно в *.gpi
  • 0
    глянул Ростов-на-Дону, в «родных» навителовских картах, «лежачих полицейских» на порядок больше, и почему то судя по карте половина города с ограничением 90 км/час.
    • +1
      помогайте наполнять — будет и здесь немало! :)
    • +1
      Вспомнить лежекопы в своем районе и нанести на карту — 15-20 мин работы, просто покликать точки на карте. :)
  • 0
    travelgps.com.ua вроде как успешно и спидкамы и лежекопы и направления для навитела в своей карте Украины делает.
    Причём не только для Навитела.
    И на ихнем форуме всё расписано, где, как и зачем.
  • 0
    А они согласованы как нибудь с проектом от stas_symba? У него вроде побольше объем файла, а если оставить оба, боюсь будут дубли.
    • 0
      На данный момент я написал ему письмо по поводу согласований/сотрудничества. Ждём реакции. :)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.