В Deck.gl визуализация строится вокруг преобразования координат данных в координаты экрана через цепочку матричных преобразований. Базовая схема включает три пространства:
Ключевую роль в этом процессе играет матрица проекции и матрица вида
(view matrix). В Deck.gl они инкапсулируются в объектах типа
Viewport и View.
Deck.gl предоставляет несколько стандартных режимов через
COORDINATE_SYSTEM:
LNGLAT — географические координаты (WGS84)LNGLAT_OFFSETS — географические координаты с локальными
смещениямиMETER_OFFSETS — метры относительно точки отсчётаCARTESIAN — произвольная декартова системаКаждый режим определяет, как данные интерпретируются до попадания в матрицу проекции WebGL.
Для картографических приложений используется
WebMercatorViewport, реализующий проекцию:
Проекционная матрица включает нелинейное преобразование:
Это ограничивает применение в задачах вне геоданных, что и приводит к необходимости пользовательских projection modes.
Пользовательские проекции необходимы при работе с:
Основная цель — заменить стандартное преобразование координат на произвольное математическое отображение.
В Deck.gl проекция определяется через связку:
View — описание камеры и логики наблюденияViewport — конкретная реализация матрицБазовый класс Viewport содержит методы:
project(position) — перевод в экранные координатыunproject(pixel) — обратное преобразованиеgetProjectionMatrix()getViewMatrix()Переопределение этих методов позволяет реализовать собственную систему координат.
Пользовательский 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:
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];
}
}
Здесь фактически заменяется математическая модель пространства.
Deck.gl ожидает, что результат проекции будет согласован с:
projectionMatrixviewMatrixviewProjectionMatrixВ пользовательских системах часто требуется переопределение:
getProjectionMatrix() {
return this.customProjectionMatrix;
}
getViewMatrix() {
return this.customViewMatrix;
}
Для нестандартных систем координат матрицы могут формироваться
вручную через gl-matrix.
Даже без кастомного View можно расширять поведение через
coordinateSystem:
import {COORDINATE_SYSTEM} from '@deck.gl/core';
new ScatterplotLayer({
coordinateSystem: COORDINATE_SYSTEM.CARTESIAN,
data,
getPosition: d => d.position
});
Для более сложных систем используется комбинация:
COORDINATE_SYSTEM.CARTESIANВ WebGL pipeline Deck.gl передаёт позицию в вершинный шейдер:
attribute vec3 positions;
uniform mat4 modelViewProjectionMatrix;
void main() {
gl_Position = modelViewProjectionMatrix * vec4(positions, 1.0);
}
При пользовательской проекции возможны два подхода:
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];
}
}
Такая модель используется при визуализации:
Deck.gl поддерживает несколько View одновременно. Это
позволяет комбинировать разные системы координат:
Каждый View имеет собственный viewport:
new Deck({
views: [
new PolarView({id: 'polar'}),
new MapView({id: 'map'})
]
});
Одна из ключевых проблем кастомных projection modes — несоответствие масштабов:
Для устранения несоответствий применяются:
При кастомных проекциях возникают типичные проблемы:
unprojectОсобенно критично:
Интерактивность требует синхронизации:
viewState)При кастомной проекции требуется переопределять:
unprojectDeck.gl использует пиксельный picking buffer, но при кастомной проекции возможна необходимость дополнительного слоя логики:
layer.pickable = true;
onHover: info => {
const worldPos = viewport.unproject(info.pixel);
}
При нестандартной геометрии может потребоваться:
Расширенный подход — комбинация нескольких систем:
Это реализуется через цепочку:
data coords → base projection → custom transform → view matrix → screen
Для высокопроизводительных сцен применяются:
unprojectОсобенно критично при: