В экосистеме HERE Technologies визуализация на карте строится вокруг слоёв, которые формируют единое графическое пространство поверх тайловой подложки. Поверх стандартных растровых и векторных слоёв активно применяется HTML5 Canvas, позволяющий создавать динамические визуальные элементы без привязки к DOM-дереву карты и без ограничения стандартными маркерами.
В контексте HERE Maps API for JavaScript canvas-слои выступают инструментом низкоуровневой отрисовки, обеспечивающим максимальную гибкость при работе с пользовательской графикой: тепловыми картами, потоками движения, анимациями, сложными геометрическими проекциями и высокочастотными обновлениями данных.
Архитектура отображения карты строится как стек слоёв:
Canvas-слой занимает промежуточное положение между объектной моделью карты и пиксельным буфером. В отличие от маркеров и полилиний, каждый пиксель canvas управляется напрямую, что исключает накладные расходы на большое количество объектов сцены.
Ключевая особенность заключается в том, что canvas не хранит геометрические объекты в структуре карты — он хранит только изображение текущего кадра.
Canvas-слой создаётся как отдельный HTML-элемент, синхронизированный с состоянием карты:
Каждое изменение состояния карты вызывает перерасчёт координат и перерисовку содержимого canvas.
Основная проблема, которую решает интеграция — преобразование географических координат в экранные пиксели и обратно.
Работа canvas-слоя невозможна без системы трансформаций:
Типичный процесс преобразования:
В HERE Maps JavaScript API для этого используется объект viewport карты, предоставляющий методы конвертации координат.
Canvas обычно создаётся как отдельный слой поверх карты:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
map.getElement().appendChild(canvas);
Размер canvas синхронизируется с размером viewport:
function resizeCanvas() {
const size = map.getViewPort().getSize();
canvas.width = size.w;
canvas.height = size.h;
}
Критически важно учитывать devicePixelRatio, иначе изображение становится размытым на высокоплотных экранах:
const ratio = window.devicePixelRatio || 1;
canvas.width = size.w * ratio;
canvas.height = size.h * ratio;
ctx.scale(ratio, ratio);
Canvas-слой не обновляется самостоятельно. Перерисовка привязывается к событиям карты:
Типовая модель:
map.addEventListener('mapviewchange', redraw);
map.addEventListener('mapviewchangeend', redraw);
В высоконагруженных сценариях используется requestAnimationFrame, чтобы избежать избыточных перерисовок:
function scheduleRedraw() {
requestAnimationFrame(redraw);
}
Основная операция canvas-слоя — перевод набора географических точек в пиксельные координаты.
const point = map.geoToScreen({ lat, lng });
ctx.fillRect(point.x, point.y, 2, 2);
Для линий и полигонов применяется последовательное преобразование массива координат:
ctx.beginPath();
path.forEach((coord, i) => {
const p = map.geoToScreen(coord);
if (i === 0) ctx.moveTo(p.x, p.y);
else ctx.lineTo(p.x, p.y);
});
ctx.stroke();
Даже внутри одного canvas логически формируются подслои:
Порядок отрисовки строго определяет визуальную иерархию.
Canvas-слои чувствительны к частоте обновления. Основные ограничения:
Оптимизация достигается через:
Если геометрия не меняется, пересчёт координат можно избежать.
Перерисовываются только изменённые области:
ctx.clearRect(dirty.x, dirty.y, dirty.w, dirty.h);
Снижение количества вызовов fill/stroke:
Canvas особенно эффективен при тысячах объектов, где объектная модель карты становится узким местом.
Сценарии применения:
При этом объекты не становятся частью сцены карты — они остаются пикселями.
Тепловые карты строятся через накопление плотности точек:
const gradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
gradient.addColorStop(0, 'rgba(255,0,0,0.8)');
gradient.addColorStop(1, 'rgba(255,0,0,0)');
ctx.fillStyle = gradient;
ctx.fillRect(x - radius, y - radius, radius * 2, radius * 2);
Динамические эффекты реализуются через непрерывный цикл перерисовки:
function animate() {
updateState();
redraw();
requestAnimationFrame(animate);
}
Состояние отделяется от визуализации, чтобы избежать привязки логики к отрисовке.
Canvas не имеет встроенной модели событий по объектам, поэтому используется обратная логика:
map.addEventListener('tap', (e) => {
const p = e.currentPointer.viewportX;
const q = e.currentPointer.viewportY;
hitTest(p, q);
});
Для сложных сцен применяются пространственные индексы (quadtree, grid indexing).
Canvas-слой чувствителен к изменению масштаба карты. При увеличении zoom:
Масштабирование часто реализуется через коэффициент:
const scale = Math.pow(2, zoom - baseZoom);
Canvas-слой не заменяет объектные слои, а дополняет их:
Типичная архитектура комбинирует оба подхода:
Несмотря на гибкость, присутствуют ограничения:
Canvas остаётся CPU-ориентированным инструментом, что определяет его место в архитектуре визуализации.
На практике применяются несколько шаблонов:
Canvas полностью перекрывает карту и синхронизируется с viewport.
Canvas работает как часть комбинированной сцены вместе с объектными слоями.
Несколько canvas-слоёв разделяют разные типы данных: