Мы выпустили редактор стилей. Подробно о том, как с ним можно настроить карту под задачи сервиса, можно почитать на vc.ru. На Хабре же хотим рассказать о концепции StyleZoom, которую мы используем в том числе и в редакторе стилей.

Под катом — небольшой рассказ о зум-левелах: почему в стандартном виде они не подходят для больших территорий и как масштаб влияет на загрузку тайлов и внешний вид карты.




Зум-левел


Для обозначения масштаба в WebGL-карте 2ГИС, как и во многих других картах, используется число — зум-левел или просто зум. Зум, равный нулю, соответствует масштабу карты, при котором весь мир помещается в квадрат размером 256?256 px.


Карта мира при zoom = 0

Увеличение зума на единицу соответствует растяжению карты в два раза. На первом зуме весь мир будет размером 256 ? 2 = 512 px. На четвёртом получаем размер 256 ? 2 ? 2 ? 2 ? 2 = 4096 px.

Такая система позволяет обозначать диапазон масштабов удобными для понимания числами. Например, zoom = 11 — примерно один крупный город на экране, zoom = 19 позволяет детально рассмотреть здания и проходы между ними.

Проекция Меркатора


В картах 2ГИС используется картографическая проекция Меркатора. Картографическая проекция — способ отобразить сферическую поверхность Земли на плоской карте.

Так как плоскость и шар — не одно и то же, любая картографическая проекция искажает форму или размеры объектов. В проекции Меркатора объекты на больших широтах на карте выглядят больше, чем объекты на экваторе. Поэтому на таких картах Гренландия выглядит размером с Африку, хотя фактически её площадь меньше площади Африки в 14 раз. Здесь можно рассмотреть, как проекция искажает размеры стран.


Если Россию приблизить к экватору, её размер на карте значительно уменьшается

Растяжение объектов пропорционально 1 / cos(lat), где lat — широта объекта. Из этой формулы следует, что объекты на широте Санкт-Петербурга (lat = 60°) на карте будут растянуты в два раза. А объекты на Северном или Южном полюсах (lat = 90°) и вовсе растянутся до бесконечности. Именно поэтому на картах в проекции Меркатора никогда не рисуются полюса — на них обрезается всё севернее и южнее ?85° широты.

Подробнее о проекции Меркатора можно почитать в этом наглядном и увлекательном материале.

Проблемы с зумом и проекцией Меркатора


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

Сравним скриншоты карты на 14-м зуме в Мурманске (широта 69°) и Ташкенте (широта 41°).


15-й зум в Мурманске


15-й зум в Ташкенте

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

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

Решение


Для решения этой проблемы мы ввели понятие styleZoom, который может отличаться от обычного zoom.
Обычный zoom StyleZoom
Определяет масштабирование объектов Определяет, какие тайлы грузить
Записывается в url Определяет, для какого масштаба применять стили
Используется в привычных методах getZoom / setZoom Используется в методах getStyleZoom / setStyleZoom
Совпадает с растровыми тайлами

styleZoom вычисляется из zoom и широты по сдедующей формуле: styleZoom = zoom + log2(1 / (2 * cos(lat)).

Из формулы вытекают следующие свойства styleZoom:
  • На широте 60° styleZoom = zoom. Так как стили изначально писались на Новосибирск и Москву, решили взять эту широту за базовую.
  • На широтах <60° styleZoom < zoom. На экваторе styleZoom = zoom — 1.
  • На широтах >60° styleZoom > zoom.

Посмотрим теперь, как будут выглядеть Ташкент и Мурманск при styleZoom = 15.


Ташкент, styleZoom = 15 (zoom ? 15.59)


Мурманск, styleZoom = 15 (zoom ? 14.53)

Хорошо видно, что styleZoom соответствует реальному масштабу карты вне зависимости от широты: дома визуально не отличаются по размеру, карта в двух разных городах выглядит одинаково.

Ограничения


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

zoom < 9
На небольших зумах, когда на экране виден весь мир или его бoльшая часть, перетаскивание карты вверх-вниз приводит к большим изменениям широты и, соответственно, styleZoom.

Во время перетаскивания это может привести к загрузке новых тайлов, переключению стилей, появлению или исчезновению объектов и т.п. Чтобы избежать этого эффекта, при zoom < 9 коррекция отключается, и styleZoom приравнивается к zoom.

lat > 60°
На очень больших широтах styleZoom становится сильно больше zoom. Так как styleZoom отвечает за то, какие тайлы грузить, может оказаться, например, что на 14-м зуме мы загрузим и отобразим тайлы 16-го зума. Тайл 16-го зума в 16 раз меньше по площади, чем тайл 14-го зума. И если обычно на экран попадает ?30 тайлов, то в этом случае их будет 480. А количество тайлов сильно влияет на производительность. Чтобы не загружать видеокарту на этих широтах, при lat > 60° коррекция отключается, и styleZoom также приравнивается к zoom.

Вместо вывода


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

За пост отдельное спасибо Лёше Федосову. Лёха, привет!