Как создать легенду
Много лет назад я участвовал в бреветах: велосипедных марафонах, где поодиночке и неспеша (но с ограничением по времени) нужно проехать от 200 до 1200 километров. Медитативное занятие, когда есть время насладиться природой, напеть все песни, рассмотреть легенду и карту маршрута… Которые удручают: часто это обычная таблица из экселя с беспорядочным оформлением, а карта факультативна. Петербуржский клуб «Балтийская звезда» продвинутее многих: участники его бреветов всегда получали карту — правда, тёмно-серую, с тёмно-серым маршрутом и КП на чёрных выносках. Не очень удобно. А поскольку я увлёкся OpenStreetMap, я почувствовал, что в силах улучшить дизайн легенд. И приступил ко второй попытке (первую, шестилетней давности, стыдно вспоминать).
Главная характеристика OpenStreetMap: безграничные возможности и чёрная дыра вместо готовых решений. Чтобы наложить маршрут на карту, нужно сделать не только маршрут, но и карту. Все без исключения существующие стили на базе OSM либо не подходят для отображения маршрута, либо вопиюще отвратительны, и накладывать на них лучше плотный лист картона. Поэтому делаю привычные действия: устанавливаю mapnik, postgis, osm2pgsql, osmosis, качаю 25-гигабайтную планету, вырезаю область, загружаю в базу. Данные есть, теперь нужно их оформить.СтильВ 2010 году в MapBox придумали формат CartoCSS и выпустили красивый редактор TileMill: по сути, конвертер CartoCSS → Mapnik XML, интерфейс к мапнику плюс редактор стиля с подсветкой. Это кардинально изменило подготовку картостилей: теперь не нужно писать XML руками или изучать ещё более заковыристые «упрощающие» аналоги: главной заслугой MapBox был даже не формат CartoCSS, а подробная документация на каждый его ключ и удобный мультиплатформенный установщик. Теперь, действительно, картостиль может создать каждый, с нуля и до впечатляющей карты.Учебники есть на ГИС-Лабе и на хабре, но они помогут лишь разобраться с кнопочками. Нет сложностей в раскрашивании линий из базы данных, но важно точно знать, что именно отображать на карте, и идеально разбираться в специфике исходных данных. В OpenStreetMap это непросто: например, поди отрисуй застроенные территории. OSM — карта для редакторов, а не для пользователей, модель данных в ней местами совершенно жуткая. Задачу генерализации до сих пор нормально никто не решил, поэтому на мелких масштабах всё равно придётся отрисовывать каждый изгиб мелкой речушки и каждый путь сортировочной станции ж/д. Так что да, главное — знать точно, какие объекты отрисовывать (меньше — лучше, универсальный принцип), и окунуться в вики и в примеры, дабы понять, как их выделить из месива данных.
Цель понятна: сделать стиль для печати на чёрно-белом принтере, в масштабах 9–11 (2–5 км в сантиметре). Важны основные дороги, их покрытие ни к чему, и максимум населённых пунктов. И горизонтали. С последними интересно: к какому решению приходят стилеписатели, покопавшись полчаса в интернете? Конечно, взять SRTM, обработать gdal_contour и плюхнуть на карту как есть. Это несложно, инструкций десятки, и на выходе получается примерно такое:
Пользоваться такими горизонталями невозможно. Их так много, что они превращаются в равномерный неинформативный фон. К тому же, обрезанный по 60 широте. Когда я поинтересовался, как быть, на форуме ГИС-Лаба подали идею: зачем SRTM на таких мелких масштабах, когда можно взять менее точный рельеф, но покрывающий весь земной шар? В итоге я нашёл великолепный GMTED2010, соединил несколько его квадратов и дополнительно сгладил в ГИС SAGA. Изолинии же нужно готовить в gdal_contour, потому что он правильно направляет линии в полученном векторном файле, позволяя нарисовать бергштрихи: маленькие засечки, показывающие направление уклона. Я добавил их «because I can», но позже понял, что без бергштрихов изолинии не имеют смысла: как отличить овраг от насыпи, если подписаны только горизонтали, кратные 100 м?
Необходимость наложения линии маршрута обусловила вынос всех подписей в сторону от дорог. Печать в мелком масштабе требует подписи даже деревень там, где обычно обозначены только крупные города. А плотность точек на дюйм у принтера позволила уменьшить размеры шрифтов на 1–2 пункта относительно других веб-карт. Эта запись в штосме обращает внимание на детали, делающие получившийся картостиль «Veloroad» лучшим для своей задачи. Сюда вынесу самое важное замечание к будущим авторам карт: никогда, слышите, никогда не рисуйте станции метро в точках station=subway. Десятки новичков каждую неделю перетаскивают подземные платформы под вестибюли станций, потому что на карте osm.org эти подземные станции отмечаются на удобных масштабах 12+, а непосредственно входы в метро — только 18+. Некоторые детали моего стиля уже начали проникать в чужие, и я надеюсь, этот аспект тоже распространится.
Лирическое отступление: рисование под рендерер — бич проекта OpenStreetMap. Большинство участников, в том числе 99% новичков, воспринимают редактор карты как графический редактор, и заливают площади: территорию города накрывают landuse=residential вместо невидимого place=*, потому что она становится приятно серой, мусорные баки обозначают amenity=recycling, потому что правильный waste_disposal не отображается на osm.org. Доходит до абсурда: парки обозначают контуром из highway=trunk, ибо он зелёненький. Один индус овладел «векторным редактором» в совершенстве, добавив на карту объем (это двумерная картинка):
Стиль «Veloroad» можно сравнить с другими на демонстрационном сайте. После него просмотр альтернатив (включая карты Яндекса) затруднён: из глаз текут кровь и слёзы, бескрайняя пустота обескураживает. Есть только два стиля, сопоставимые с этим или превосходящие его: это «MapSurfer» с божественной расстановкой подписей и «Чепецк.net» с изобилием данных. Все они, а также снимки, треки, общественный транспорт и прочее, включены в список слоёв openstreetmap.ru.
Тайлы Какой смысл в картостиле, который нельзя показать всем своим знакомым? Не хвастаться нельзя, это помогает двигаться к цели. К тому же, раз у меня получился такой офигенный стиль, нужно дать людям возможность его использовать в любом виде. Так что я, изучив предложения, взял десятидолларовый сервер в DigitalOcean (10$ за 30 гигов SSD! взрывает мозг) и развернул на нём тайл-сервер. Инструкций по разворачиванию масса: Как можно догадаться, ссылок много, потому что ни одна из них не исчерпывающа. Технологии меняются быстрее, чем их описывают: стиль osm.org давно переехал на CartoCSS, renderd получил альтернативу в виде Tirex, многие пакеты давно лежат в системных репозиториях. Как бы обычна ни была ваша операционка (т.е. убунту), всё равно при настройке сервера (начиная с mod_tile) придётся пошаманить, и качество настройки сервера будет зависеть от вашей способности находить и обрабатывать информацию.Я загрузил маленький регион, всего на 17 гигабайт, но обновлял его ежеминутными диффами всей планеты. Это давало прирост в 650 мегабайт в сутки. Каждые десять дней место на диске заканчивалось, приходилось пересоздавать базу на локальной машине и загружать на сервер: его ресурсов не хватало для запуска osm2pgsql. На четвёртой заливке терпение иссякло, и я засел за программирование. Результат выложил на гитхаб: скрипт для обрезки планетного диффа по полигону и базе данных osm2pgsql. Он неидеален, но дневной прирост упал вчетверо. Раз в месяц уж можно запустить перезаливку. Идеальным вариантом, как заметил один француз в почтовой рассылке, было бы научить osm2pgsql читать дополненные диффы и не хранить промежуточные данные в базе (они занимают около 60% объёма).
Несмотря на то, что в базе были целиком Северо-Западный, Центральный и Дальневосточный округа, каждый второй просил добавил его город, неожиданно вдалеке от границ обрезки. С запросами помог справиться маппер и админ Self-Perfection, разделивший бремя оплаты сервера, благодаря чему я увеличил базу ещё на 10 гигабайт. Теперь на карте не хватает только Южного и Северо-Кавказского округов, но и спрос оттуда невелик. А, и Крыма тоже пока нет.
Уже не помню, кто в нашем чатике заикнулся о ретиновых тайлах. В тот момент я внезапно осознал: на своём ноутбуке из-за избыточного количества пикселей я смотрю все сайты в масштабе 133%. Текст выглядит хорошо, а вот карты — сплошное мыло (google maps — приятное исключение). Я уже поднаторел в настройке мапника — почему бы не попробовать сделать тайлы в формате 2:1 для режима HiDPI? Одна ма-а-аленькая проблема: единственная демонстрация таких тайлов на моей памяти работала на Tirex, а у меня — более старый renderd. В общем, последовал мой первый коммит в проект на C++. На «Veloroad Retina» я иногда захожу просто так, без нужды: на чётких линиях и качественных шрифтах отдыхает глаз, и кажется, что у растровых карт есть будущее.
Печать Возвращаемся к задаче. Данные загружены в базу, мапник настроен, стиль написан, нужно получить карту заданного прямоугольника в векторном и растровом форматах в 300 dpi для печати на странице A5. Простая операция, где инструмент? О, для рендеринга картинки мапником скриптов аж полдюжины — от простого generate_image, где координаты углов области нужно прописывать в исходнике, до nik2img, который умеет разные проекции и форматы. Вот только миллиметры никто не принимает, а попробовав для проверки запросить снимок карты с заданным зумом в заданной точке, я получил совсем не то, что вижу на osm.org. С разрешением тоже проблема: есть параметр scale_factor, но если его менять, только линии становятся толще, закрывая всё вокруг. Что за фигня, как этим пользуются?! И опять, сделай сам. Я засел за питон и за неделю сделал крутой скрипт Nik4. Довольно простой, если не считать изрисованных схемами листов и разворошённых исходников мапника. Главное — надёжность и десять килобайт иллюстрированной документации. Если знаешь, что хочешь получить, решение элементарно. От простейшего
nik4.py --url http://www.openstreetmap.org/#map=16/55.9865/37.2160 osm.xml screenshot.png
до точных настроек формата бумаги, полей, слоёв и dpi. Никаких сюрпризов, ни одного загадочного параметра. Я набросал интерактивный выбор bbox и с месяц делал карты из командной строки. Кроме того, специально для велосипедистов, предпочитающих закрытые и очень дорогие программы бесплатным, подготовил слепки карты в своём стиле на нескольких масштабах и сконвертировал для OziExplorer. Крупнейший, на 11 масштабе, имел размер 21×22 тысячи пикселей: Nik4 умеет обходить ограничения мапника, склеивая итоговую карту из фрагментов. Правда, img2ozf этого монстра не прожевал, только z10 (16k×16k).
Главной задачей было получить векторную карту в формате SVG. Как ни странно, значение dpi влияет на файл: буквы в подписях мапник расставляет по пиксельной сетке, поэтому на низких dpi кернинг страдает. Другая проблема с подписями — cairo не умеет (по словам авторов мапника, я не проверял) объединять элементы в группы, поэтому каждое слово состоит из отдельных букв, плюс их подсветка (halo). Алгоритм расстановки подписей у мапника тупой, как бревно, поэтому после экспорта хорошо бы сдвинуть подписи с дорог и маршрута, —, но это же 2×кол-во букв кривых, которые очень быстро надоедает выделять. Обратно к питону: скрипт mapnik-group-text собирает буквы подписей и их halo в группы.
В начале июня для конкурса плакатов на конференции «State of the Map EU» выгрузил огромный SVG с окрестностями Карлсруэ в стиле veloroad. Редактировать файлы размером более 100 мегабайт в Inkscape тяжело, поэтому не стал двигать подписи, только добавил плашку с текстом. Плакат занял пятое место в голосовании, но первым пропал с выставки: немудрено, он там был самым практичным. Я успел вторым: стырил яркий «Pop Art», который теперь висит у меня на стене.
Кнопка Командная строка — хорошо, но нормальным пользователям нужен GUI. Я же не для себя всю эту систему делаю, а для велосипедистов: они должны с минимумом усилий получать легенды и карты, чтобы это было ненамного сложнее, чем раньше, с экселем и скриншотами OziExplorer. Вышеупомянутые слепки уже позволяют делать скриншоты, но можно ещё упростить, по популярному алгоритму «нажми на кнопку — получишь результат». Раньше я для простых сайтов обращался к php (веб) или perl (cgi), но раз уж начал писать питоновские скрипты, пошёл изучать cgi-программирование на нём. Сервис «Get Veloroad» — самое эпичное, на мой взгляд, картографическое приложение в русском интернете в этом году. Для тех, кому нужен снимок карты, по крайней мере. По сути, это веб-интерфейс к Nik4: те же параметры размера, полей и формата, плюс несколько параметрических полей: слой GPX и линейный масштаб. В отличие от тайлов, дополнительные стили не требуют места под кэш, поэтому я добавил несколько стилей на выбор: тот же veloroad, но без принудительной русификации, и стандартный стиль osm.org. Другими словами, теперь не нужно делать снимки экрана или склеивать тайлы: достаточно выбрать нужную область, размер картинки, формат png, и нажать кнопку. Участники «народных карт», ждущие еженедельных подачек печатных карт, тут должны задуматься, на что они тратят время.
Но нет, всё равно неаккуратненько. Мешает другая особенность мапника: если ему скажешь, что хочешь картинку в 360 dpi, то размер полученного SVG будет учетверён, как будто и не было никакого dpi, а в полях width и height библиотека cairo, зачем-то, пишет размеры в пикселях с суффиксом «pt», уменьшая картинку в 1,25 раза. Разумеется, если такой SVG экспортировать в PDF, размер страницы удивит. Так что файл нужно масштабировать. Я уже готовился пересчитывать числа в каждом атрибуте каждого тега, как обнаружил несоответствие width / height и атрибута viewBox. Почитал спецификацию, и вот оно: не нужно обрабатывать весь XML, достаточно пересчитать эти шесть чисел. Обратно, к блокноту и геометрическим построениям!
Допустим, получилось, но есть ещё одна задача: когда экспортируешь картинку с полями в сантиметр, векторные объекты не обрезаются по границе, а хорошо так торчат, поэтому недостаточно расширить viewBox, нужно ещё закрыть поля белой рамкой. В идеале хорошо бы объекты обрезать самому, но это сложная и не горящая задача (и обрезка не отменяет рамки, потому что толстые штрихи будут торчать). Ещё пара дней расчёта координат, и скрипт svg-resize готов и внедрён в Get Veloroad: теперь, скачивая карту для А5 с полями 7 мм в SVG, после экспорта файла в PDF вы получите именно это: белые поля и точный размер страницы. Конечно, это не относится к прямому скачиванию PDF с сайта.
Автоматическое изменение размера попутно решило другую проблему: набор графических элементов для оформления бреветов (маркеры старта, финиша и КП, название, логотип и стрелочки) при импортировании на лист больше не нужно масштабировать, достаточно разгруппировать, растащить на нужные места и поправить текст. Делать карту маршрута стало быстрее и приятнее, и весь процесс построен на открытых программах.
Карта не важна бреветчикам: имея на руках только её, несложно заблудиться на каком-нибудь запутанном перекрёстке. Самое главное — понятная легенда, словесное описание маршрута с чётким направлением движения на каждом повороте и расстояниями между точками. Пошаговые автоматические описания не подойдут: через 10 часов кручения педалей хочется не гадать, какой из правых поворотов имелся в виду, а ехать по знаку «Кукуево 50 ►». До меня легенды печатались из Excel, были излишне пространными, и оттого — лист А4 же не приделаешь на руль — случайная муха могла закрыть собой абзац. Каждый новый автор маршрута рисовал таблицу с нуля, иногда даже рассчитывая время открытия-закрытия КП на калькуляторе.Шаблон Построение легенды отнимает уйму времени. Даже не выезжая на трассу, а просто открыв в одном окне панорамы Google, в другом — сайт OSRM для расчёта расстояний, можно потратить полдня на поиск уникальных признаков поворота, формулирование недвусмысленных фраз, форматирование таблицы. Облегчить это можно, формализовав процесс, чтобы путешествие по панорамам отнимало не 40, а 80% времени. Поэтому я сделал шаблон легенды в LibreOffice, с использованием открытого шрифта PT Sans. Конечно, нехорошо заставлять людей устанавливать левые программы, но лучше сразу положиться на открытые форматы, чем выкладывать файлы в проприетарном xls с зависимостью от шрифта Microsoft, который запрещено распространять лицензией.Дальше всё просто, помогает въевшаяся в мозг анимированная картинка про упрощение таблиц. Убрал сетку, но выделил чётные строки светло-серым, чтобы проще было отслеживать в дороге: светлый поворот, тёмный поворот. Убрал лишние столбцы, вписав нужную информацию (номер КП, например) прямо в текст. Сделал обязательным описание самого поворота, а то некоторые норовят оставить только киломераж и направление: байкеры теперь не проедут указатель. Текст напечатан десятым размером, километры — ещё крупнее. Выделяются только ключевые слова: «налево», «направо», «КП». Все легенды собираются в архив, откуда можно скопировать куски маршрутов, и посмотреть, как правильно обозначать ту или иную ситуацию (Т-образные перекрёстки, номера дорог в Финляндии, родники и т.п.).
На втором листе таблицы автоматически рассчитываются отметки времени для каждого поворота: когда на точке нужно быть волонтёрам, чтобы поймать первых лосей, и когда точку нужно закрывать по регламенту ACP. На прохождение бреветов есть нормативы: от 13,5 часов на 200 км до 90 на 1200. До 600 км время легко посчитать, разделив расстояние на 15 км/ч. Скорость «лосей», самых быстрых велосипедистов, считается 32—34 км/ч на первом этапе, и далее по убывающей. Разумеется, на длинных бреветах нельзя забывать про дату. Вычисленные значения копируются в описание КП и финиша. Закончив, легенду нужно экспортировать в PDF.
PDF Теперь у нас есть PDF легенды и один или несколько PDF с картами, все в формате A5, как их будут использовать велосипедисты. В принтеры же обычно загружают листы A4, поэтому нужно не только объединить несколько файлов в один, но и попарно склеить страницы, повернув при необходимости. Пользователям Windows, которыми, судя по форматам файлов, являются все марафонцы поголовно, здесь придётся изворачиваться: хотя перетасовать страницы можно в PDF Split and Merge, склеить их — куда более сложная задача. Поначалу я делал шаблон из двух колонок, а картинку с картой раздваивал, но этот подход годился лишь для коротких бреветов, легенда к которым умещалась на одном листе A5. Как быть? Для Linux во всех репозиториях есть волшебные программы pdftk и pdfjam, которые решают любые задачи подготовки PDF, какие могут прийти в голову. С месяц я их использовал, но не прикажешь же нормальным людям ставить виртуалку только для подготовки пары документов. А pdfjam, кстати, подтягивает зависимостей на полгигабайта. Без создания веб-интерфейса не обойтись. Сначала я решил срезать угол: поместил на страницу четыре формы заливки PDF и строку для тасования страниц в pdftk («A1 B A2 C» — первая страница документа A, документ B, вторая страница… ну, понятно). По кнопке cgi-скрипт вызывал pdftk и pdfjam, и возвращал готовый PDF. Часто кривой, потому что не допустить ошибки в строке pdftk с первого раза сложно. А инструкция для авторов пополнилась тремя абзацами описания формата, плюс ссылкой на PDFsam и его описанием, как запасного варианта для самых занятых. Не дело.
Формально задача решена: авторам достаточно установить две программы и один шрифт, и весь процесс проходим от начала до конца. Тут бы и сказочке конец, отписаться в личку и до свидания, уже написал текст письма…, но нет, не могу. Неаккуратненько. Убеждая себя, что хотя бы Flask выучу, создал новый каталог и запустил vim. Целую неделю глядел на документацию фреймворка (очень хорошую, кстати), стащил пару строк из учебника на хабре, лениво писал шаблоны и копипастил куски из cgi-крипта. В определённый момент: «ба, да оно почти готово» — и за два вечера быстро закончил, оттестировал, выложил на сервер, закончил ещё раз и перевыложил. Получилось офигенно: уметь нужно только на кнопку нажимать, да отличать лево и право; вместо строки для pdftk — визуальный редактор, все документы валидируются, и можно в качестве страниц использовать картинки. Без ImageMagick, конечно, не обошлось: им создаются миниатюры страниц, а растровые карты конвертируются в PDF. С помощью identify определяется необходимость поворота страниц, поэтому в интерфейсе никаких смен ориентации, только переворот. Система, правда, закрыта на пароль, потому что диск не резиновый, а ресурсы другим процессам важнее.
Архив карт и легенд, короткая и подробная инструкции и все ссылки собраны на странице для авторов, ссылку на которую я разослал тем шести авторам, с которыми взаимодействовал в этом году. До конца года осталось всего пять бреветов, легенды к которым я сделаю сам, чтобы нарастить базу. В одном из них надеюсь и поучаствовать. Проект считаю завершённым, хотя его успешность будет понятна только в следующем году: будут ли авторы пользоваться моими сервисами, или вернутся к экселю и OziExplorer? Печатные карты нынче никому не нужны, напомнили мне в чатике OpenStreetMap. Туристы берут с собой навигаторы, матёрые туристы покупают атлас с картами «под генштаб», а обывателям достаточно веб-карт на их смартфонах. Петербуржские еженедельные пятничные ночные велопокатушки публикуют карты маршрутов скорее как дань традиции: используемые ими подложки нечитаемы, но никто не жалуется: достаточно ехать за ведущим и сотнями красных маячков, не потеряешься. Когда я в последний раз раскрывал атласы в кармане пассажирского сидения моей машины? Не помню.
Но есть занятие, для которого возможность оперативной печати карт жизненно необходима. В прямом смысле: это гуманитарная помощь и спасательные работы. В Африке, в Индонезии, на Гаити, в Пакистане тысячи врачей, спасателей, просто добровольцев рисуют и обновляют OpenStreetMap, чтобы затем распечатать и использовать в работе. В странах третьего мира нельзя полагаться на батарейки и интернет-канал. Для сбора картографических данных они используют «обходные листы»: маленькие фрагменты карты в очень большом масштабе, поверх которых рисуют недостающие объекты. Честно говоря, таким же примитивным способом данные собираются и в России, и в Германии, и в Великобритании. При этом до сих пор нет удобного способа напечатать обходные листы впрок. В процессе работы над подготовкой печатных карт я изучил немало релевантных технологий, и это поможет мне в будущем оптимизировать процесс печати обходных листов, уже без многогигабайтных серверов и сложных компонентов. Скоро всем этим людям будет достаточно нажать на кнопку.