Пользовательские projection modes

Роль проекций в архитектуре Deck.gl

В Deck.gl визуализация строится вокруг преобразования координат данных в координаты экрана через цепочку матричных преобразований. Базовая схема включает три пространства:

  • Model space — локальные координаты данных
  • World space — глобальная система координат слоя
  • Screen space — пиксельные координаты канваса

Ключевую роль в этом процессе играет матрица проекции и матрица вида (view matrix). В Deck.gl они инкапсулируются в объектах типа Viewport и View.


Встроенные системы координат

Deck.gl предоставляет несколько стандартных режимов через COORDINATE_SYSTEM:

  • LNGLAT — географические координаты (WGS84)
  • LNGLAT_OFFSETS — географические координаты с локальными смещениями
  • METER_OFFSETS — метры относительно точки отсчёта
  • CARTESIAN — произвольная декартова система

Каждый режим определяет, как данные интерпретируются до попадания в матрицу проекции WebGL.


WebMercator как базовая проекция

Для картографических приложений используется WebMercatorViewport, реализующий проекцию:

  • долгота/широта → плоская проекция Меркатора
  • масштабирование через zoom level
  • центрирование через longitude/latitude центра

Проекционная матрица включает нелинейное преобразование:

  • искажение по оси Y при приближении к полюсам
  • логарифмическое поведение масштаба

Это ограничивает применение в задачах вне геоданных, что и приводит к необходимости пользовательских projection modes.


Причины создания пользовательских projection modes

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

  • CAD и инженерными системами
  • игровыми мирами с нестандартной геометрией
  • полярными и сферическими координатами
  • многомерными или абстрактными пространствами
  • визуализациями, где WebMercator неприменим

Основная цель — заменить стандартное преобразование координат на произвольное математическое отображение.


Механизм View и Viewport

В Deck.gl проекция определяется через связку:

  • View — описание камеры и логики наблюдения
  • Viewport — конкретная реализация матриц

Базовый класс Viewport содержит методы:

  • project(position) — перевод в экранные координаты
  • unproject(pixel) — обратное преобразование
  • getProjectionMatrix()
  • getViewMatrix()

Переопределение этих методов позволяет реализовать собственную систему координат.


Создание пользовательского View

Пользовательский projection mode чаще всего реализуется через наследование от View.

Пример: полярная система координат.

import {View, WebMercatorViewport} from '@deck.gl/core';

class PolarView extends View {
  constructor(props) {
    super(props);
  }

  makeViewport({width, height, viewState}) {
    return new PolarViewport({
      width,
      height,
      ...viewState
    });
  }
}

Реализация кастомного Viewport

Основная логика проекции задаётся в Viewport:

import {Viewport} from '@deck.gl/core';

class PolarViewport extends Viewport {
  projectPosition([radius, angle, z]) {
    const x = radius * Math.cos(angle);
    const y = radius * Math.sin(angle);

    return [x, y, z];
  }

  unprojectPosition([x, y, z]) {
    const radius = Math.sqrt(x * x + y * y);
    const angle = Math.atan2(y, x);

    return [radius, angle, z];
  }
}

Здесь фактически заменяется математическая модель пространства.


Интеграция с матрицами WebGL

Deck.gl ожидает, что результат проекции будет согласован с:

  • projectionMatrix
  • viewMatrix
  • viewProjectionMatrix

В пользовательских системах часто требуется переопределение:

getProjectionMatrix() {
  return this.customProjectionMatrix;
}

getViewMatrix() {
  return this.customViewMatrix;
}

Для нестандартных систем координат матрицы могут формироваться вручную через gl-matrix.


Использование COORDINATE_SYSTEM в пользовательских слоях

Даже без кастомного View можно расширять поведение через coordinateSystem:

import {COORDINATE_SYSTEM} from '@deck.gl/core';

new ScatterplotLayer({
  coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
  data,
  getPosition: d => d.position
});

Для более сложных систем используется комбинация:

  • COORDINATE_SYSTEM.CARTESIAN
  • пользовательская трансформация данных
  • pre-transform в CPU или vertex shader

Шейдерный уровень кастомной проекции

В WebGL pipeline Deck.gl передаёт позицию в вершинный шейдер:

attribute vec3 positions;

uniform mat4 modelViewProjectionMatrix;

void main() {
  gl_Position = modelViewProjectionMatrix * vec4(positions, 1.0);
}

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

  1. CPU трансформация — координаты преобразуются до передачи в GPU
  2. GPU трансформация — кастомная математика внутри шейдера

GPU подход предпочтителен для динамических сцен.


Пример: сферическая проекция

Сферическая система координат:

class SphericalViewport extends Viewport {
  projectPosition([theta, phi, r]) {
    const x = r * Math.sin(phi) * Math.cos(theta);
    const y = r * Math.sin(phi) * Math.sin(theta);
    const z = r * Math.cos(phi);

    return [x, y, z];
  }
}

Такая модель используется при визуализации:

  • планетарных данных
  • космических симуляций
  • 3D-навигации

Многовидовые (multi-view) пользовательские проекции

Deck.gl поддерживает несколько View одновременно. Это позволяет комбинировать разные системы координат:

  • карта + 3D сцена
  • глобус + локальная декартова область
  • полярная диаграмма + линейный график

Каждый View имеет собственный viewport:

new Deck({
  views: [
    new PolarView({id: 'polar'}),
    new MapView({id: 'map'})
  ]
});

Согласование масштабов и единиц измерения

Одна из ключевых проблем кастомных projection modes — несоответствие масштабов:

  • WebMercator использует нелинейный zoom
  • Cartesian часто линейный
  • сферические системы требуют нормализации радиуса

Для устранения несоответствий применяются:

  • uniform scale factors
  • normalization функций
  • логарифмические преобразования

Численная стабильность и ограничения

При кастомных проекциях возникают типичные проблемы:

  • потеря точности при больших координатах
  • дрейф при обратных преобразованиях unproject
  • переполнение float32 в GPU
  • артефакты при пересечении границ координатных систем

Особенно критично:

  • системы с большими радиусами (астрономические данные)
  • высокие zoom levels
  • смешанные coordinate systems в одном рендере

Использование кастомных projection modes в интерактивных сценах

Интерактивность требует синхронизации:

  • камеры (viewState)
  • матриц проекции
  • событий мыши

При кастомной проекции требуется переопределять:

  • обработку pointer events
  • преобразование координат курсора через unproject
  • hit-testing в пользовательском пространстве

Hit-testing в пользовательской системе координат

Deck.gl использует пиксельный picking buffer, но при кастомной проекции возможна необходимость дополнительного слоя логики:

layer.pickable = true;

onHover: info => {
  const worldPos = viewport.unproject(info.pixel);
}

При нестандартной геометрии может потребоваться:

  • обратное преобразование через аналитические формулы
  • использование BVH в пользовательском пространстве

Гибридные projection modes

Расширенный подход — комбинация нескольких систем:

  • географическая база (LNGLAT)
  • локальные декартовы смещения (METER_OFFSETS)
  • пользовательская нелинейная трансформация

Это реализуется через цепочку:

data coords → base projection → custom transform → view matrix → screen

Оптимизация кастомных проекций

Для высокопроизводительных сцен применяются:

  • предрасчёт матриц на CPU
  • кэширование viewport-инстансов
  • минимизация вызовов unproject
  • перенос вычислений в vertex shader

Особенно критично при:

  • десятках тысяч объектов
  • частых zoom/pan событиях
  • анимациях трансформации пространства