О наших смотрелках для Яндекс-карт

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

Яндекс-карты несомненно замечательный сервис. С достаточно развитой инфраструктурой проекта, мощным, порою даже излишне, функционалом и богатой экосистемой, сложившейся вокруг них. Но проект таких габаритов тоже имеет определённые слабости. Например, невозможность равномерного развития, которая и стала спусковым крючком для помянутого потока писем. А истинной, хтонической причиной проблемы стал сделанный некогда инженерами проекта стратегический выбор. Имя этому выбору: WGS84 + non-TMS.

Скачать смотрелку



Если смысл аббревиатур от вас ускользнул - не беспокойтесь, это нормально. Сейчас переведу на русский.

  1. Выбор проекции для расчётов диктовался, по всей видимости, соображениями прогрессивности и поддержкой сообщества энтузиастов WGS84 (EPSG:4326). Помимо общей красоты и смелости данного шага, Яндекс отсекал на 96% использование чужих тайлов на своих картах. А весь прочий мир веб-картографии преспокойно жил в проекции Sphere Mercator (EPSG:53004) и особо не заморачивался в отношении какого-то регионального картографического сервиса для Турции, Белоруссии, Украины и России. Справедливости ради следует сказать, что поддержка Sphere Mercator в Яндекс-картах есть. Именно это, в итоге, и позволяет решать проблему сторонних тайлов.
  2. TMS - это ориентация оси Y в сетке тайлов. В этом случае нуль сетки начинается от экватора и далее, возрастая по модулю Y-индексы тайла, смещаются к полюсам. Дело в том, в веб-картографии изображение так или иначе надо приводить к прямоугольным тайлам. В силу этого обрезаны высокие широты, где искажения становятся чудовищными в силу одной только математики. И нуль сетки тайлов в верхнем углу карты, оттого, становится непрактичен.

А иногда возникает потребность создать свои собственные тайлы для своих карт: схемы зданий, планы участков, гигапиксельную фотографию на зумы разобрать. А то и нарисовать свою собственную планету. Тут потребуются уже весьма специфичные и специальные знания и умения. Яндекс для удобства таких пользователей выпустил утилиту-тайлер (Tiler). Маленькая и надёжная она отлично режет маленькие карты. После небольших шаманств пользовательские карты водружаются поверх базовых карт и все довольны.

Но лишь до тех пор, пока карта не становится сколько-нибудь крупной. Начиная с размера где-то 15к на 15к пикселей, что очень немного, утилита выдаёт ошибку и карт не делает. Обновления утилиты не было. Очевидно Яндекс не признал направление разработки тайлера перспективным. Итоги таковы: описанная грустная история и почти полное отсутствие в экосистеме Яндекс-карт карт пользовательских.

Впрочем, следует сказать что подготовка своих тайлов - дело хлопотное. Но порою без них не обойтись. Для изготовления карт, размещённых на этом сайте, используется техпроцесс, базирующийся на пакете OSGEO (и GDAL, в частности). Этот пакет позволяет сделать топопривзяку растров на местность, используя аэрофотосъёмку, предоставляемую провайдерами сервисов. Затем последовательный запуск ряда утилит командной строки, оформленный в пакетный файл, позволяет сделать нарезку получившихся файлов GeoTIFF в тайлы Яндекс-карт. Или, как альтернативное применение, порезать огромное изображение на тайлы для просмотра с возможностью зума.

Поскольку OSGEO и GDAL проекты международные (а Яндекс - всё же, увы, но местечковый), на самом последнем этапе нарезки используются лучшие международные практики: тайлы подготавливаются для отображения в проекции Sphere Mercator и с Y-ориентацией сетки тайлов в TMS. Таким образом, результат подключения "в лоб", нарезанных при помощи GDAL тайлов, становится немного... э-э-э... обескураживающе обескураживающ.

Выглядит это так:

Это было обстоятельство номер один.

Обстоятельство номер два. В Интернете для решения проблемы подключения своих тайлов к Яндексу распространяется скрипт TilerConverter.js. Как несомненно прогрессивная вещь, он позволяет удобно подключить пользовательские слои, сам инстанциирует карту с надлежащими параметрами и, что очень радует, - действительно показывает пользовательские слои карт.

Но какая же ложка мёда без бочки дёгтя? Вот, например одно из полей объекта конфигурации:
tileUrlTemplate: "http://example.com/%z/%x/%y.png",
вот здесь мы и приплыли - вылезай, сливай керосин.

Что сразу бросается в глаза:
  1. Фиксированный темплейт для URL тайлов. При желании, конечно же, можно его поменять, особенно если утилита-тайлер почему-то складывает изображения в другие папки. Можно переставить сегменты местами. Ну, а как быть, если у нас тайлы оказались порезаны в TMS-сетке? Когда над каждым тайлом нужно "на лету" проводить арифметическую операцию?
  2. Зачем нужен скрипт, который дублирует основной, хорошо документированный функционал Яндекс-карт, не давая взамен ничего нового?

- Да ладно! - скажут оптимисты, - Всё же работает.
Работает, да. Только платой за эту работу становится утрата целостности среды Яндекс-карт и отрицание того, что: "но, ведь, у всех людей разные лица!".



При этом, надо заметить, что API Яндекс-карт позволяет безо всяких сторонних скриптов отличнейшим образом выводить любые пользовательские тайлы. Далее приведена функция для отображения слоя тайлов на "чистом" API карт Яндекса.

Отсюда начинаются пояснения к коду, находящемуся в ymaps.js.

Ключевым пунктом является функция, обрабатывающая имена тайлов:
ymaps.layer.storage.add('your#map', function () {  //    (1)
  return new ymaps.Layer(function (tile, zoom) {
    return zoom + "/" + tile[0] + "/" + (dX[zoom] - tile[1]) + ".png";    //    (2)
  }, { tileTransparent: 1, zIndex: 1000 });
});

Суть происходящего тут в следующем: в хранилище слоёв помещается функция, возвращающая URL тайла. Вторым параметром передаётся объект свойств этого слоя, он универсален для практически любого случая.

Следует акцентировать внимание на безымянную функцию (1) выше. Она является сердцем механизма для вывода тайлов, подготовленных GDAL (gdal2tiles.py) под управлением Яндекса. В неё передаются собственные координаты тайла и зум.
Возвращает она очень хорошо контролируемую прототипом строку с URL тайла (2), над которым можно производить операции на лету.
Здесь dX[] - хэш коэффициентов, связывающий значение зума и значение поправки для переориентации тайлов из TMS в сетку Яндекса. Задаётся в самом начале функции init() следующим кодом:
var dX = [];
 for (var a=0; a < 21; a++){
  dX[a] = Math.pow(2, a) - 1;
}

При пересчёте из TMS-ориентации истинный индекс каждого тайла по оси Y находится вычитанием TMS-индекса из (2^zoom - 1). Таким образом, данная реализация позволяет включить поддержку тайлов, сгенерированных в TMS, в Яндекс-картах.

После создания слоя его следует внести в список доступных просмотрщику карт:
ymaps.mapType.storage.add('your#map', new ymaps.MapType('Карта', ['your#map'] ));
а также можно добавить и в пользовательский селектор карт.

Ну и напоследок требуется правильно задать проекцию карты для расчётов:
map = new ymaps.Map("YMapsID",
{center: [-94.6392198798,-40.7223029247], zoom: 6, type: 'your#map', behaviors: ["scrollZoom","drag"]},
{projection: ymaps.projection.sphericalMercator, maxZoom: 7, minZoom: 0, checkZoomRange: false});

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