![image](https://habrastorage.org/web/75b/895/29e/75b89529eeb7416b829a613b25491cd3.png)
Сначала общее и банальное: оба фреймворка при действиях пользователя изменяют внутренне состояние карты ( или внутреннее состояние изменятся само от callback или по таймеру ), а оно уже даёт команду на перерисовку.
Как рисует openlayers
Openlayers использует два варианта рисования — Canvas и WebGL ( DOM рендер для тайлов и изображений был удален из современной 3-й версии). Компонент map создает div, в котором и трудятся два этих рендера. Сильно упрощенная последовательность вызовов примерно такая:
- В компоненте карты вызывается функция renderFrame
- Определяются слои, которые видимы на данном масштабе
- Каждый из видимых слоев рисует своим рендером изображение. Помимо разделения на по типам драйверов (canvas и webgl), далее рендеры делятся еще и по типам слоев (ol.renderer.canvas.TileLayer, ol.renderer.canvas.VectorLayer и тд).
- К полученным изображениям слоев добавляется прозрачность, трансформации и вырезание ( 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шной производительностью поиграть с пикселями в тайлах. Вот пример раскраски пикселей в тайлах по широте:
![](https://habrastorage.org/webt/59/d9/a6/59d9a64d46db9290512060.png)
Или вот от того же автора Leaflet.GLMarkers позволяет рисовать большие объемы точек на карте (маркеры могут быть анимированы), хотя выглядит кривовато, особенно нерабочая анимация зумирования напрягает.
![](https://habrastorage.org/webt/59/d9/aa/59d9aa79ac685788162211.gif)
Бывают тяжелые случаи и у плагинов, которые обслуживаются серьезными компаниями. Например, esri-leaflet в DynamicMapLayer при каждом рефреше создает новый слой ImageOverlay, причем создает его когда загрузилась соответствующая картинка, т.е. порядок слоев на карте постоянно перемешивается, работать с z-index невозможно. Вашему покорному слуге даже пришлось делать улучшенный вариант данного слоя, также там есть переход через 180 меридиан, которого нет в оригинальном плагине.
Еще одна частая проблема: разная поддержка плагинами 2х основных версий leaflet — 0.7 и 1.0. Между ними очень много критических изменений, и плагины могут не работать в конкретной версии. Бывает еще так: один плагин работает только в версии 1.0, а другой нужный нам плагин автор еще не обновил или совсем забил на его обслуживание ( хотя сейчас таких все меньше) и он работает только под старой.
Короче говоря, основная проблема рисования leaflet, то что каждый плагин делает все как ему заблагорассудится, кто-то создает html img, кто-то рисует в canvas, кто-то webgl'ом рисует в canvas в тайл и тд. И дальнейшая работа с этим в комплексе становится проблематичной.
P.S.: Следующая статья будет про источники картографических данных и возможности работы с ними в каждом из фреймворков, а так же финальные выводы о том, какой фреймворк выбрать именно в вашем случае.
Комментарии (5)
sshikov
09.10.2017 18:33Оба рендера работают неплохо, однако, если переключитесь на webgl, то стоит помнить, что ресурсы (например картинки тайлов) нужно будет пускать через прокси, иначе получите:
Хм, правда? Я понимаю, что задачи разные бывают, но как по мне — это был бы почти блокер. Это ведь означает, что без сервера вы работать не можете. При этом работа без сервера с малыми нагрузками вполне воможна.
И еще вопрос — а что с SVG? Если я верно понял, их кто-то должен рендерить либо в канвас, либо в WebGL. Кто именно и в какой момент? Означает ли это, что в итоге будет потеряна значительная часть функциональности, например анимации? И насколько при этом теряется производительность?
SergeyVoyteshonok Автор
10.10.2017 09:16Хм, правда? Я понимаю, что задачи разные бывают, но как по мне — это был бы почти блокер. Это ведь означает, что без сервера вы работать не можете. При этом работа без сервера с малыми нагрузками вполне воможна.
Обычно все популярные открытые тайловые сервисы (OSM и тд) поддерживают cross-origin, а если вы используете какой-то сервис без него, то скорее всего можно нарушить соглашение об использовании, если это ваш сервис, то просто включите на нем cross-origin.
По поводу SVG, если вы именно хотите загрузить вектор из SVG, то оба фреймворка умеют делать и слои и маркеры из SVG, честно говоря, анимацию проверить не удалось. Leaflet'овые плагины вроде используют родной SVG браузера, а вот OL да, рисует на canvas.sshikov
10.10.2017 20:59Ну, наверное да. Практически все равно рабочих сервисов не так много, их можно по пальцам двух рук пересчитать.
А что до второго — я не вижу возможности как следует поддерживать SVG, если рендерит в канвас или WebGL какой-то сторонний компонент. Это довольно сложный стандарт, его и браузеры-то не всегда как следует умеют (не полностью или с багами). И это обидно (хотя наверное зависит от задач — у меня просто каждый второй заказчик непременно хочет SVG маркеры, да еще и с анимацией, в виде разного рода диаграмм).
dom1n1k
Пользуясь случаем, задам ещё вопрос к системе плагинов 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, а именно системы плагинов.
Но возможно, я что-то недопонимаю?
SergeyVoyteshonok Автор
Да, это как раз один из примеров, автор второго плагина должен был делать расширение к L.TileLayer, а не городить потомка. Что-то типа: