Особенности масштабирования WebGL-карты

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

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

mmsiu17-cenqrzfmztsd5ypjcpo.png

Зум-левел


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

badmvus5jwi0mke7ain5spe5x9i.png
Карта мира при zoom = 0

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

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

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


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

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

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

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

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

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


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

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

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

nkpjjv9rxd-vysol-9x8zuobwsy.png
15-й зум в Ташкенте

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

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

Решение


Для решения этой проблемы мы ввели понятие styleZoom, который может отличаться от обычного zoom.
styleZoom вычисляется из zoom и широты по сдедующей формуле: styleZoom = zoom + log2(1 / (2 * cos (lat)).

Из формулы вытекают следующие свойства styleZoom:

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


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

pdgo_4eypdhaml2b_8wid87oxlg.png
Ташкент, styleZoom = 15 (zoom ≈ 15.59)

ez5iuu8svhtpef_ovovmqs59vim.png
Мурманск, styleZoom = 15 (zoom ≈ 14.53)

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

Ограничения


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

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

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

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

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


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

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

© Habrahabr.ru