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

    Мы выпустили редактор стилей. Подробно о том, как с ним можно настроить карту под задачи сервиса, можно почитать на 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
    На небольших зумах, когда на экране виден весь мир или его бóльшая часть, перетаскивание карты вверх-вниз приводит к большим изменениям широты и, соответственно, styleZoom.

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

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

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


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

    За пост отдельное спасибо Лёше Федосову. Лёха, привет!
    2ГИС
    Главные по городской навигации

    Comments 10

      +1
      Интересно.
      Захотелось даже проверить есть ли у Яндекс Карт такая адаптация.
      • UFO just landed and posted this here
          0

          Я думаю, что такое "умное" масштабирование можно реализовать и без WebGL.
          могу ошибаться, никогда не смотрел как оно все внутри устроено.

            +2
            По-моему, такая ситуация была 10 лет назад :) Технологии, браузеры и компьютеры пользователей постоянно улучшаются. И большинство крупных картографических проектов в вебе уже сейчас так или иначе используют WebGL (2GIS, Google, Here, MapBox, Yandex).

            • UFO just landed and posted this here
                +3
                Не забудьте еще про стабильную технологию HTML :)

                Всё таки Applе пометили deprecated только OpenGL. Речи про WebGL не идет, и это в тот же момент подтвердил их лид разработки Dean Jackson. Разработка WebGL 2 идет, недавно он был включен по умолчанию в Safari Technology Preview.

                Говорить о том, как будет в итоге работать WebGPU, кажется, еще рано, технология не готова.
                  0
                  Недавно добавили и уже откажутся? Не успели технологию допилить у уже не получает обновлений? Да вы, батенька, сказочник
                0
                Что такое отрисовка карт на CSS? Карты совершенно точно никто в HTML не верстает.
                Возможно, вы спутали с MapCSS/CartoCSS, но это всего лишь названия спецификаций стилей карты, и к стандарту CSS они имеют отношение только тем, что синтаксически на него похожи. Да и MapBox уже отказался от своей CSS-подобной спецификации в пользу более новой. А рендерит MapBox тоже при помощи всё того же WebGL.
                И растровых карт уже тоже не осталось ни у Гугла, ни у Яндекса. Точнее, остались, но только для обратной совместимости.
                • UFO just landed and posted this here
                    0
                    Тайлы-то векторные, а не растровые. А это всё сильно меняет :)
                    У вас тогда остаётся только вариант напрямую рисовать на canvas. И CSS тут никак не поможет.

                    Открываю карты Google. В DOM вижу canvas. В инструментах разработчика в Performance есть строчка с GPU. Может, у вас, в вашем браузере и в вашей системе WebGL на их картах отключен, но у меня — нет.

            Only users with full accounts can post comments. Log in, please.