Leaflet 1.x.x vs Openlayers 4.x.x. Часть 2. Как рисуются карты

    В прошлой статье обещал, что эта будет про сообщества и плагины, но потом решил, что правильней будет сначала рассказать о том, как в данных фреймворках рисуются карты. Начну с Openlayers, потом Leaflet, потом его плагины.

    image

    Сначала общее и банальное: оба фреймворка при действиях пользователя изменяют внутренне состояние карты ( или внутреннее состояние изменятся само от callback или по таймеру ), а оно уже даёт команду на перерисовку.

    Как рисует openlayers


    Openlayers использует два варианта рисования — Canvas и WebGL ( DOM рендер для тайлов и изображений был удален из современной 3-й версии). Компонент map создает div, в котором и трудятся два этих рендера. Сильно упрощенная последовательность вызовов примерно такая:

    1. В компоненте карты вызывается функция renderFrame
    2. Определяются слои, которые видимы на данном масштабе
    3. Каждый из видимых слоев рисует своим рендером изображение. Помимо разделения на по типам драйверов (canvas и webgl), далее рендеры делятся еще и по типам слоев (ol.renderer.canvas.TileLayer, ol.renderer.canvas.VectorLayer и тд).
    4. К полученным изображениям слоев добавляется прозрачность, трансформации и вырезание ( clip, если есть) и они все рисуются в общем контексте карты.

    Оба рендера работают неплохо, однако, если переключитесь на webgl, то стоит помнить, что ресурсы (например картинки тайлов) нужно будет пускать через прокси, иначе получите:
    Uncaught DOMException: Failed to execute 'texImage2D' on 'WebGLRenderingContext': The cross-origin image at may not be loaded.

    Если сервер поддерживает cross-origin, а ошибку все равно выдает поставьте в настройках источника изображения crossOrigin: 'anonymous'

    Как рисует Leaflet


    Для начала, вот хорошая схема классов, которая отображается опять же через leaflet.
    Leaflet для рисования карт создает несколько панелей (Pane), которые являются DOM элементами (div), с которыми уже работают слои.
    Название Описание zIndex
    mapPane основная панель содержит в себе все остальные auto
    tilePane панель для тайловых слоев 200
    overlayPane панель для векторный слоев 400
    shadowPane панель для теней 500
    markerPane панель для маркеров 600
    tooltipPane панель для подсказок 650
    popupPane панель для появляющихся данных по объектам 700

    И тут кроется первая интересная деталь, если в Openlayers нумерация по zIndex сквозная для всех слоев по умолчанию, то в Leaflet, слои получаются сгруппированными. Если вы вдруг хотите, чтобы ваш тайловый слой когда-нибудь был «над» векторным, вам надо или принудительно указывать у конкретного тайлового слоя в options pane: overlay, или если сквозная нумерация нужна у всех слоев, то складывать их все в один. Кстати, можно создавать свой pane и необязательно он должен быть внутри mapPane.

    А дальше начинается самое интересное, если в Openlayers для всех слоев, используется одинаковый набор рендеров, то Leaflet, каждый из слоев сам выбирает как и чем ему рисоваться. Например тайловые слои и ImageOverlay рисуются только DOM-элементами (хотя в плагинах есть вариации на тему рисования тайлов с использованием WebGl и Canvas). Для векторных слоев слоев используется SVG или Canvas, если оба поддерживаются браузером, по умолчанию используется SVG.

    Особенности рисования в разных плагинах Leaflet


    Как я уже сказал, несмотря на то, что из коробки у Leaflet нет webgl рендера, некоторые плагины используют эту технологию. Leaflet.TileLayer.GL, например, создает вместо изображения тайла canvas, в который рисует рендер Webgl. С помощью данного плагина можно с GPUшной производительностью поиграть с пикселями в тайлах. Вот пример раскраски пикселей в тайлах по широте:



    Или вот от того же автора Leaflet.GLMarkers позволяет рисовать большие объемы точек на карте (маркеры могут быть анимированы), хотя выглядит кривовато, особенно нерабочая анимация зумирования напрягает.



    Бывают тяжелые случаи и у плагинов, которые обслуживаются серьезными компаниями. Например, esri-leaflet в DynamicMapLayer при каждом рефреше создает новый слой ImageOverlay, причем создает его когда загрузилась соответствующая картинка, т.е. порядок слоев на карте постоянно перемешивается, работать с z-index невозможно. Вашему покорному слуге даже пришлось делать улучшенный вариант данного слоя, также там есть переход через 180 меридиан, которого нет в оригинальном плагине.

    Еще одна частая проблема: разная поддержка плагинами 2х основных версий leaflet — 0.7 и 1.0. Между ними очень много критических изменений, и плагины могут не работать в конкретной версии. Бывает еще так: один плагин работает только в версии 1.0, а другой нужный нам плагин автор еще не обновил или совсем забил на его обслуживание ( хотя сейчас таких все меньше) и он работает только под старой.

    Короче говоря, основная проблема рисования leaflet, то что каждый плагин делает все как ему заблагорассудится, кто-то создает html img, кто-то рисует в canvas, кто-то webgl'ом рисует в canvas в тайл и тд. И дальнейшая работа с этим в комплексе становится проблематичной.

    P.S.: Следующая статья будет про источники картографических данных и возможности работы с ними в каждом из фреймворков, а так же финальные выводы о том, какой фреймворк выбрать именно в вашем случае.
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 5
    • +1
      Пользуясь случаем, задам ещё вопрос к системе плагинов Leaflet.
      Там принято под узкие задачи создавать отдельные классы, наследуя их от стандартных.
      Суть проблемы проще пояснить на примере.

      Скажем, есть общий класс L.TileLayer. А для специфических провайдеров делается что-то в таком духе:
      L.TileLayer.Provider = L.TileLayer.extend({ ... });
      А используется так:
      L.tileLayer.provider('Stamen.Watercolor').addTo(map);
      Это плагин плагин leaflet-providers и пока как будто бы всё логично.

      Но идём дальше.
      Есть, скажем, плагин TileLayer.Grayscale, которые переводит тайловый слой в оттенки серого.
      Внутри у него то же наследование:
      L.TileLayer.Grayscale = L.TileLayer.extend({ ... });
      Используется аналогично:
      L.tileLayer.grayscale(...).addTo(map);

      Внимание вопрос: а как их использовать вместе? Что, если я хочу использовать слой Stamen.Watercolor и чтобы он стал черно-белым? Я не знаю. И это лишь один из примеров. Мне это кажется серьезным архитектурным изъяном — изъяном не самого Leaflet, а именно системы плагинов.

      Но возможно, я что-то недопонимаю?
      • 0
        Да, это как раз один из примеров, автор второго плагина должен был делать расширение к L.TileLayer, а не городить потомка. Что-то типа:
        L.TileLayer.prototype.makeGrayScale = function() {...}
        
      • 0
        Оба рендера работают неплохо, однако, если переключитесь на webgl, то стоит помнить, что ресурсы (например картинки тайлов) нужно будет пускать через прокси, иначе получите:

        Хм, правда? Я понимаю, что задачи разные бывают, но как по мне — это был бы почти блокер. Это ведь означает, что без сервера вы работать не можете. При этом работа без сервера с малыми нагрузками вполне воможна.


        И еще вопрос — а что с SVG? Если я верно понял, их кто-то должен рендерить либо в канвас, либо в WebGL. Кто именно и в какой момент? Означает ли это, что в итоге будет потеряна значительная часть функциональности, например анимации? И насколько при этом теряется производительность?

        • 0
          Хм, правда? Я понимаю, что задачи разные бывают, но как по мне — это был бы почти блокер. Это ведь означает, что без сервера вы работать не можете. При этом работа без сервера с малыми нагрузками вполне воможна.


          Обычно все популярные открытые тайловые сервисы (OSM и тд) поддерживают cross-origin, а если вы используете какой-то сервис без него, то скорее всего можно нарушить соглашение об использовании, если это ваш сервис, то просто включите на нем cross-origin.

          По поводу SVG, если вы именно хотите загрузить вектор из SVG, то оба фреймворка умеют делать и слои и маркеры из SVG, честно говоря, анимацию проверить не удалось. Leaflet'овые плагины вроде используют родной SVG браузера, а вот OL да, рисует на canvas.
          • 0

            Ну, наверное да. Практически все равно рабочих сервисов не так много, их можно по пальцам двух рук пересчитать.


            А что до второго — я не вижу возможности как следует поддерживать SVG, если рендерит в канвас или WebGL какой-то сторонний компонент. Это довольно сложный стандарт, его и браузеры-то не всегда как следует умеют (не полностью или с багами). И это обидно (хотя наверное зависит от задач — у меня просто каждый второй заказчик непременно хочет SVG маркеры, да еще и с анимацией, в виде разного рода диаграмм).

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