Дебаг рендеринга

Рендеринг карты в OpenLayers основан на многоуровневой системе: Map → View → Layers → Sources → Renderers. Каждый слой может использовать собственный механизм отрисовки (Canvas, WebGL, DOM для overlay), а итоговый кадр собирается в рамках одного цикла frame state.

Ключевая особенность: карта не перерисовывается «по DOM-событию» напрямую. Вместо этого используется модель запросов рендера, где изменения состояния приводят к постановке кадра в очередь.


Диагностика жизненного цикла кадра

Рендер проходит через последовательность состояний:

  • изменение view (центр, zoom, resolution)
  • обновление layer source (тайлы, векторные данные)
  • триггер render event
  • сборка frameState
  • отрисовка слоёв

Критический объект для анализа — frameState. Он содержит:

  • текущую проекцию
  • разрешение (resolution)
  • extent (границы видимой области)
  • время анимации
  • список активных слоёв

Отладка начинается с проверки того, обновляется ли frameState при ожидаемых действиях.


Инструменты инспекции render-событий

OpenLayers предоставляет события уровня карты:

map.on('precompose', (event) => {
  console.log('precompose', event.frameState);
});

map.on('postcompose', (event) => {
  console.log('postcompose', event.frameState);
});

При анализе проблем:

  • отсутствие precompose → карта не перерисовывается
  • есть precompose, но нет видимого результата → проблема в renderer или стилях
  • есть оба события, но нет слоя → проблема в source/layer visibility

Типовые причины отсутствия рендера

1. View не инициирует обновление

map.getView().setCenter([0, 0]);
map.getView().setZoom(5);

Если изменения выполняются без триггера:

view.setProperties({}, true);

рендер может не запускаться.


2. Несовпадение проекций

Наиболее частая проблема — несоответствие EPSG:

  • View: EPSG:3857
  • Source: EPSG:4326

Векторные данные могут не отображаться без трансформации:

import {transform} from 'ol/proj';

const coords = transform([lon, lat], 'EPSG:4326', 'EPSG:3857');

3. Extent вне видимой области

Если геометрия вне view.extent, слой не участвует в отрисовке.

Отладка:

console.log(view.calculateExtent());

Отладка tile-слоёв

Tile layers зависят от загрузки сетки.

Проблемные сценарии:

  • тайлы не запрашиваются
  • тайлы запрашиваются, но не отображаются
  • отображаются пустые квадраты

Инструменты проверки:

tileLayer.getSource().on('tileloadstart', e => console.log('start', e.tile.getKey()));
tileLayer.getSource().on('tileloaderror', e => console.log('error'));
tileLayer.getSource().on('tileloadend', e => console.log('end'));

Если tileloadstart отсутствует — проблема в resolution/grid.


Vector rendering debug

Vector rendering зависит от:

  • style function
  • feature geometry validity
  • render order
  • zIndex

Проверка стилей

const layer = new VectorLayer({
  style: (feature) => {
    console.log(feature.getGeometry().getType());
    return defaultStyle;
  }
});

Если функция не вызывается — слой не участвует в кадре.


Проблемы с Canvas renderer

Canvas может «молчать» при:

  • opacity = 0
  • visibility = false
  • display none у контейнера
  • неправильный pixelRatio

Проверка pixel ratio:

console.log(window.devicePixelRatio);

Принудительная диагностика:

map.renderSync();

WebGL renderer debugging

WebGL-слои требуют отдельного анализа:

  • контекст WebGL может быть потерян
  • атласы текстур не загружены
  • буферы геометрии не обновлены

Симптом: карта есть, но слой пустой.

Проверка:

map.getLayers().forEach(l => {
  console.log(l.getRenderer());
});

Принудительный рендер и invalidation

OpenLayers использует ленивую систему перерисовки. Для диагностики полезны:

map.render();
map.renderSync();

Разница:

  • render() — асинхронный кадр
  • renderSync() — немедленная отрисовка

Также:

layer.changed();
source.changed();

Отладка через render events

Дополнительные точки контроля:

map.on('rendercomplete', () => {
  console.log('frame done');
});

Если событие не вызывается — рендер цикл не завершён.


Проблемы с стилями и z-index

Слои могут «исчезать» из-за порядка отрисовки:

new TileLayer({
  zIndex: 10
});

Векторный слой с меньшим zIndex может перекрываться тайловым.


Координатные трансформации как источник артефактов

Ошибки преобразования часто выглядят как:

  • смещение объектов
  • исчезновение геометрии
  • «разъезд» точек и линий

Контроль:

import {toLonLat, fromLonLat} from 'ol/proj';

console.log(toLonLat([x, y]));

Анализ frameState

Расширенная диагностика:

map.on('postrender', (evt) => {
  const fs = evt.frameState;
  console.log({
    resolution: fs.viewState.resolution,
    center: fs.viewState.center,
    extent: fs.extent
  });
});

Ключевые признаки:

  • resolution = undefined → view не инициализирован
  • пустой extent → проблема projection/view
  • постоянный одинаковый frameState → нет триггера обновления

HiDPI и размытость рендера

Размытая карта без ошибок рендера часто связана с:

  • несоответствием canvas size и CSS size
  • неправильным pixelRatio

Проверка:

const map = new Map({
  pixelRatio: window.devicePixelRatio
});

Проблемы с overlay-слоем

DOM overlays не участвуют в canvas render loop.

Типовые ошибки:

  • overlay не обновляет позицию при pan
  • элементы «залипают»

Диагностика:

map.on('postrender', () => {
  overlay.setPosition(feature.getGeometry().getCoordinates());
});

Прерывание рендер-цикла

Рендер может быть заблокирован:

  • бесконечными исключениями в style function
  • тяжелыми синхронными вычислениями
  • утечками событий change

Проверка через изоляцию слоя:

map.getLayers().clear();

Производственная стратегия дебага

При сложных проблемах рендера используется последовательная изоляция:

  1. базовая карта (OSM tile layer)
  2. добавление view без interaction
  3. подключение одного слоя
  4. добавление источника данных
  5. включение стилей
  6. добавление событий

Каждый шаг фиксируется через precompose/postcompose и frameState.


Диагностика анимаций и transition effects

Анимации могут маскировать ошибки:

view.animate({
  center: [0, 0],
  duration: 1000
});

Если кадры не обновляются — проблема в animation queue.


Контроль загрузки ресурсов

Network debugging показывает:

  • tile requests
  • vector JSON loads
  • style assets

Отсутствие запросов почти всегда указывает на upstream проблему (view/layer/source), а не на сеть.


Системный подход к визуальной отладке

Комбинация точек контроля:

  • precompose — вход в рендер
  • postcompose — финал слоя
  • rendercomplete — завершение кадра
  • tileload* — источник данных
  • frameState — состояние сцены

Совместный анализ этих сигналов позволяет локализовать сбой на уровне pipeline: от данных до пикселя.