Геодезические вычисления

Геодезические вычисления в контексте визуализации в deck.gl опираются на представление координат на сфере и их последующую трансформацию в экранное пространство через проекцию Web Mercator. Базовая проблема заключается в том, что географические координаты (долгота, широта) задаются на эллипсоиде WGS84, тогда как большинство визуальных операций выполняется в декартовой системе координат после проецирования.


В deck.gl исходные данные почти всегда представлены в виде пар:

  • долгота (longitude)
  • широта (latitude)

Эти значения интерпретируются как точки на сфере, где радиус Земли принимается константой, а вычисления выполняются в геодезической модели приближённо сферического типа.

Ключевое преобразование:

  • WGS84 (географические координаты)
  • → Web Mercator (EPSG:3857)
  • → экранные координаты

Web Mercator используется как стандарт де-факто для WebGL-визуализации, поскольку обеспечивает линейность локальных масштабов и удобство работы с тайловыми системами.


Проекция Web Mercator

Проекция задаётся следующими преобразованиями:

[ x = R ]

[ y = R (( + ))]

где:

  • ( ) — долгота в радианах
  • ( ) — широта в радианах
  • ( R ) — радиус Земли в системе проекции

Особенность заключается в экспоненциальном растяжении областей около полюсов, что напрямую влияет на визуальные артефакты при интерполяции геодезических линий.


Геодезическая интерполяция

Линейная интерполяция в экранных координатах приводит к искажению маршрутов. Поэтому в deck.gl применяется сферическая интерполяция.

Для двух точек (A) и (B) на сфере используется интерполяция по великому кругу:

[ (A, B, t) = A + B]

где:

  • ( ) — центральный угол между точками
  • ( t )

Такой подход обеспечивает корректное построение дуг, соответствующих кратчайшему пути на сфере.


Великие круги и ArcLayer

В слое ArcLayer геодезия реализуется через построение дуг между источником и целью.

Основная особенность:

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

Фрагмент вычисления промежуточной точки:

function interpolateGreatCircle(p0, p1, t) {
  const [lng0, lat0] = p0;
  const [lng1, lat1] = p1;

  const φ0 = lat0 * Math.PI / 180;
  const λ0 = lng0 * Math.PI / 180;
  const φ1 = lat1 * Math.PI / 180;
  const λ1 = lng1 * Math.PI / 180;

  const x0 = Math.cos(φ0) * Math.cos(λ0);
  const y0 = Math.cos(φ0) * Math.sin(λ0);
  const z0 = Math.sin(φ0);

  const x1 = Math.cos(φ1) * Math.cos(λ1);
  const y1 = Math.cos(φ1) * Math.sin(λ1);
  const z1 = Math.sin(φ1);

  const dot = x0 * x1 + y0 * y1 + z0 * z1;
  const θ = Math.acos(Math.min(Math.max(dot, -1), 1));

  const sinθ = Math.sin(θ);

  const a = Math.sin((1 - t) * θ) / sinθ;
  const b = Math.sin(t * θ) / sinθ;

  const x = a * x0 + b * x1;
  const y = a * y0 + b * y1;
  const z = a * z0 + b * z1;

  return [
    Math.atan2(y, x) * 180 / Math.PI,
    Math.asin(z) * 180 / Math.PI
  ];
}

Декартова аппроксимация и ошибки линейной интерполяции

Прямолинейная интерполяция:

lng = lng0 + (lng1 - lng0) * t;
lat = lat0 + (lat1 - lat0) * t;

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

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

deck.gl избегает этого через использование геодезических преобразований в слоях, где важна точность траектории (ArcLayer, GreatCircleLayer).


Обработка антимеридиана

Проблема возникает при переходе через ±180° долготы. Без коррекции линия строится через весь глобус.

Алгоритм нормализации:

  • если |Δλ| > 180°, выполняется сдвиг координат
  • долгота приводится к диапазону [-180°, 180°]

Пример логики:

function normalizeLngDiff(lng0, lng1) {
  let delta = lng1 - lng0;
  if (delta > 180) lng1 -= 360;
  if (delta < -180) lng1 += 360;
  return lng1;
}

Это предотвращает разрыв дуги при визуализации потоков между Азией и Америкой.


Altitude и псевдо-геодезическая высота

В deck.gl часто используется параметр altitude для визуального поднятия линий над поверхностью карты.

Важно различать:

  • геодезическую высоту (реальная высота над эллипсоидом)
  • визуальную высоту (экструзия в WebGL сцене)

В слоях типа ArcLayer высота задаётся как скаляр:

getHeight: d => d.value * scaleFactor

Это значение не влияет на геодезию, но влияет на z-буферизацию и глубину сцены.


Геодезия в GeoJsonLayer

GeoJsonLayer работает с геометриями:

  • Point
  • LineString
  • Polygon

Для LineString применяется сегментация:

  • длинные рёбра разбиваются на подотрезки
  • каждый сегмент интерполируется на сфере при необходимости

При включённой опции greatCircle:

  • линии преобразуются в дуги великого круга
  • увеличивается количество вершин
  • возрастает точность отображения маршрутов

H3 и дискретная геодезия

В связке с deck.gl используется H3-гексагональная система индексации.

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

  • пространство Земли дискретизируется в иерархическую сетку
  • каждая ячейка соответствует области на сфере
  • геодезические расстояния приближаются через граф смежности

Преимущество:

  • быстрые агрегации
  • приближённые вычисления расстояний
  • устойчивость к проекционным искажениям

Геодезические расстояния

Расстояние между двумя точками на сфере вычисляется через формулу гаверсинуса:

[ a = ^2() + (_1)(_2)^2()]

[ c = 2 (, )]

[ d = R c]

Этот метод используется при:

  • кластеризации точек
  • фильтрации дальних объектов
  • построении heatmap-подобных слоёв

Precision и численные ошибки

При работе с WebGL и float32 возникают ограничения:

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

Меры стабилизации:

  • локальное центрирование координат
  • использование origin shifting
  • вычисление в double precision на CPU с последующим приведением

Геодезические преобразования в shader pipeline

В ряде слоёв deck.gl часть вычислений переносится в vertex shader:

  • интерполяция дуг
  • высотные смещения
  • масштабирование по зуму

Типичный фрагмент GLSL-логики:

vec3 projectPoint(vec2 lngLat, float radius) {
  float lon = radians(lngLat.x);
  float lat = radians(lngLat.y);

  return vec3(
    radius * cos(lat) * cos(lon),
    radius * cos(lat) * sin(lon),
    radius * sin(lat)
  );
}

Это обеспечивает согласованность геодезической модели между CPU и GPU.


Сферическая нормализация направлений

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

  • перевод координат в 3D сферу
  • вычисление направления как cross-product
  • восстановление дуги через ортогональную плоскость

Такой подход применяется при генерации плавных кривых потоков между регионами, где важна непрерывность направления на глобусе.