Ретина дисплеи и масштабирование

Современные экраны с высокой плотностью пикселей (Retina, HiDPI) используют коэффициент масштабирования устройства, доступный через window.devicePixelRatio. Значения 2, 3 и выше означают, что один CSS-пиксель соответствует нескольким физическим пикселям матрицы дисплея.

В веб-картографии это приводит к ключевой проблеме: геометрия и растровые тайлы должны отображаться резко, без размытия, несмотря на увеличение плотности пикселей. Библиотека OpenLayers решает это через внутреннее масштабирование canvas и управление разрешением тайлов.

Основная идея заключается в разделении:

  • логического разрешения карты (CSS pixels)
  • физического разрешения устройства (device pixels)

Если игнорировать этот разрыв, карта выглядит «мыльной»: линии дорог, подписи и тайлы теряют четкость.


devicePixelRatio и базовая модель масштабирования

В браузере ключевым параметром выступает:

window.devicePixelRatio

Типичные значения:

  • 1 — обычные дисплеи
  • 2 — Retina
  • 3 — высокоплотные мобильные экраны

OpenLayers использует этот коэффициент для пересчета внутренних размеров canvas. Упрощённо:

физический размер canvas = CSS размер × pixelRatio

При этом CSS-геометрия карты остаётся неизменной, чтобы сохранить корректную работу проекций и взаимодействий.


pixelRatio в OpenLayers

В OpenLayers основной механизм управления HiDPI реализован через параметр карты:

import Map from 'ol/Map.js';
import View from 'ol/View.js';

const map = new Map({
  target: 'map',
  pixelRatio: window.devicePixelRatio,
  view: new View({
    center: [0, 0],
    zoom: 2
  })
});

Поведение pixelRatio

  • увеличивает внутреннее разрешение canvas
  • влияет на отрисовку векторных слоёв
  • корректирует выбор тайлов
  • управляет четкостью текста и символов

Если явно не задавать pixelRatio, библиотека автоматически использует window.devicePixelRatio.


Масштабирование canvas и механизм отрисовки

OpenLayers использует несколько canvas-слоёв:

  • viewport canvas (основной слой)
  • overlay canvas (векторные данные)
  • interaction canvas (взаимодействия)

При HiDPI каждый canvas масштабируется следующим образом:

  1. задаётся размер в CSS пикселях
  2. внутренний buffer увеличивается
  3. применяется transform scale для компенсации

Упрощённая схема:

CSS size: 800×600
devicePixelRatio: 2

Canvas buffer: 1600×1200
CSS rendering: 800×600

Это обеспечивает:

  • отсутствие размытия линий
  • сохранение геометрической точности
  • корректное наложение слоёв

Векторная графика и HiDPI

Векторные слои в OpenLayers критически зависят от плотности пикселей. При увеличении pixelRatio изменяются:

  • толщина линий
  • размер символов
  • радиусы точек
  • подписи (labels)

Пример стилизации с учётом плотности

import Style from 'ol/style/Style.js';
import Stroke from 'ol/style/Stroke.js';

const style = new Style({
  stroke: new Stroke({
    color: 'blue',
    width: 2 * window.devicePixelRatio
  })
});

Однако в OpenLayers чаще применяется иной подход: стиль задаётся в CSS-пикселях, а библиотека сама масштабирует геометрию при рендеринге.


Проблема размытых тайлов

Растровые тайлы — основной источник проблем на Retina-дисплеях. Если загружается тайл 256×256 и отображается на дисплее с DPR=2, он растягивается и теряет четкость.

Решение: HiDPI тайлы

Сервер может предоставлять тайлы увеличенного размера:

  • 256×256 (обычные)
  • 512×512 (2x)
  • 768×768 (3x)

OpenLayers управляет этим через tilePixelRatio.


tilePixelRatio и источники данных

Векторизация тайлов и их масштабирование регулируется на уровне источника:

import TileLayer from 'ol/layer/Tile.js';
import OSM from 'ol/source/OSM.js';

const layer = new TileLayer({
  source: new OSM({
    tilePixelRatio: window.devicePixelRatio
  })
});

Как это работает

  • при DPR = 2 запрашиваются тайлы 512×512
  • затем они уменьшаются до 256 CSS-пикселей
  • визуально это даёт более высокую резкость

WebGL и аппаратное масштабирование

В WebGL-рендерерах OpenLayers используется отдельный механизм управления плотностью пикселей.

Ключевой параметр:

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

В WebGL:

  • создаётся framebuffer с увеличенным разрешением
  • используется GPU scaling
  • минимизируется aliasing

Это особенно важно для:

  • больших объёмов векторных данных
  • анимации
  • 3D-подобных эффектов

Масштабирование текста и подписей

Текстовые подписи — наиболее чувствительный элемент к HiDPI.

Проблемы:

  • размытость при масштабировании canvas
  • некорректная ширина текста при DPR ≠ 1
  • несоответствие размеров между слоями

OpenLayers решает это через:

  • рендер текста в увеличенном canvas
  • использование measureText с компенсацией pixelRatio
  • пересчёт anchor-offsets

Пример:

import Text from 'ol/style/Text.js';

const textStyle = new Text({
  text: 'City',
  scale: 1 / window.devicePixelRatio
});

На практике чаще масштабирование скрыто внутри рендера и не требует ручного вмешательства.


CSS transform vs canvas scaling

Существует два подхода к HiDPI:

1. CSS масштабирование

  • canvas остаётся маленьким
  • увеличивается через transform: scale()
  • быстро, но размыто

2. Буферное масштабирование (используется OpenLayers)

  • canvas физически увеличивается
  • отрисовка происходит в высоком разрешении
  • результат максимально чёткий

OpenLayers использует второй подход как основной.


Контроль качества рендеринга

Качество изображения зависит от нескольких факторов:

1. Pixel ratio

Чем выше, тем четче изображение, но выше нагрузка на GPU.

2. Tile resolution

Нативное разрешение тайлов должно соответствовать DPR.

3. Anti-aliasing

Векторные слои используют сглаживание:

  • линии
  • полигоны
  • подписи

4. Canvas compositing

При наложении слоёв важно учитывать порядок и прозрачность.


Оптимизация производительности на Retina

Высокий DPR увеличивает нагрузку экспоненциально:

  • в 2 раза по ширине
  • в 2 раза по высоте
  • в 4 раза по пикселям

Практики оптимизации

Ограничение pixelRatio

const map = new Map({
  pixelRatio: Math.min(window.devicePixelRatio, 2)
});

Это снижает нагрузку на:

  • слабые GPU
  • мобильные устройства
  • старые ноутбуки

Динамическое переключение качества

const dpr = window.devicePixelRatio > 2 ? 2 : window.devicePixelRatio;

Используется компромисс между:

  • чёткостью
  • FPS
  • временем загрузки

Масштабирование в разных уровнях zoom

Retina-дисплеи усиливают визуальное восприятие zoom-уровней.

Особенность OpenLayers:

  • zoom управляет масштабом карты
  • pixelRatio управляет плотностью пикселей

Эти параметры независимы, но визуально взаимосвязаны.

При zoom-out:

  • уменьшается детализация
  • но сохраняется чёткость линий

При zoom-in:

  • возрастает нагрузка на рендер
  • увеличивается количество перерисовок

TileGrid и адаптация под HiDPI

TileGrid управляет структурой тайлов:

import TileGrid from 'ol/tilegrid/TileGrid.js';

const grid = new TileGrid({
  tileSize: 256,
  extent: [0, 0, 1000000, 1000000]
});

Для Retina часто применяется:

  • увеличение tileSize до 512
  • или использование tilePixelRatio

Это позволяет:

  • уменьшить количество перерисовок
  • повысить детализацию

Особенности поведения на мобильных устройствах

Мобильные браузеры:

  • часто имеют DPR = 3
  • используют агрессивное энергосбережение
  • ограничивают GPU ресурсы

В OpenLayers это приводит к необходимости:

  • уменьшения pixelRatio
  • упрощения стилей
  • ограничения количества слоёв

Синхронизация событий и координат

Важно, что масштабирование влияет только на рендер, но не на координаты.

OpenLayers сохраняет:

  • world coordinates (EPSG проекции)
  • screen coordinates (CSS pixels)

Преобразование:

screen = world → projection → pixelRatio scaling

Это обеспечивает:

  • корректную работу взаимодействий (click, drag)
  • точное попадание в объекты
  • стабильность событий

Итоговая модель HiDPI в OpenLayers

Архитектура работы можно описать так:

  1. координаты данных остаются неизменными
  2. viewport масштабируется через pixelRatio
  3. canvas получает увеличенный буфер
  4. тайлы подбираются с учётом DPR
  5. WebGL или 2D renderer выполняет отрисовку
  6. браузер отображает результат без потери резкости

Такой подход позволяет OpenLayers обеспечивать одинаково чёткое отображение карт на экранах с любой плотностью пикселей, сохраняя единообразие поведения слоёв, взаимодействий и геометрии.