Проецирование координат на пиксели

В Google Maps JavaScript API географические координаты (широта и долгота) не используются напрямую для отрисовки объектов на экране. Перед отображением они преобразуются в промежуточную систему координат, основанную на проекции Меркатора, а затем — в пиксельные координаты текущего viewport’а карты.

Базовая идея заключается в разделении трёх уровней представления данных:

  • географические координаты (LatLng)
  • мировые координаты (world coordinates)
  • экранные пиксельные координаты (pixel coordinates)

Каждый из этих уровней имеет собственную систему отсчёта и масштабирования, зависящий от zoom и размера тайлов.


Проекция Меркатора как основа вычислений

В основе Google Maps лежит Web Mercator Projection (EPSG:3857). Она преобразует сферические координаты в плоскость, сохраняя углы, но искажая площади.

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

y = (( + ))

где φ — широта в радианах.

Долгота преобразуется линейно:

x =

где λ — долгота в радианах.

После нормализации координаты приводятся к диапазону [0, 1], что позволяет работать с ними независимо от конкретного масштаба карты.


Мировые координаты и тайловая система

В Google Maps вся поверхность Земли на zoom level 0 представлена как один квадратный тайл 256×256 пикселей.

При увеличении масштаба используется экспоненциальное разбиение:

N = 2^z

где z — уровень масштабирования (zoom), а N — количество тайлов по оси X и Y.

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

x = ^z

y = ^z

Эта система координат называется world coordinate space и является промежуточным слоем между географией и экраном.


Переход от мировых координат к пикселям экрана

Экранные координаты зависят не только от масштаба, но и от текущего центра карты.

Если обозначить:

  • worldPoint — координата точки в мировых пикселях
  • centerPoint — мировая координата центра карты
  • mapSize — размер области отображения в пикселях

то экранная координата вычисляется как:

screen = worldPoint - centerPoint +

Таким образом, центр карты всегда фиксируется в середине viewport’а, а все остальные точки смещаются относительно него.


Использование MapCanvasProjection

В Google Maps JavaScript API доступ к преобразованиям координат осуществляется через объект MapCanvasProjection, который предоставляется внутри OverlayView.

Ключевые методы:

  • fromLatLngToPoint(latLng)
  • fromPointToLatLng(point)
  • getWorldWidth()

Пример получения пиксельных координат:

class CustomOverlay extends google.maps.OverlayView {
  onAdd() {}

  draw() {
    const projection = this.getProjection();
    const point = projection.fromLatLngToPoint(
      new google.maps.LatLng(55.751244, 37.618423)
    );

    const scale = Math.pow(2, this.getMap().getZoom());
    
    const pixelX = point.x * scale;
    const pixelY = point.y * scale;

    const div = this.div;
    div.style.left = pixelX + "px";
    div.style.top = pixelY + "px";
  }

  onRemove() {}
}

Здесь важно учитывать, что fromLatLngToPoint возвращает координаты в нормализованном пространстве, а масштабирование выполняется отдельно через 2^zoom.


Масштабирование и зависимость от zoom

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

Формально:

P_{zoom} = P_{base} ^z

где:

  • P_base — координата на уровне zoom 0
  • z — текущий zoom

Это объясняет, почему любые кастомные оверлеи должны пересчитываться при изменении масштаба карты.


Учет смещения карты (pan)

При перемещении карты происходит изменение центра viewport’а, что приводит к перерасчёту всех экранных координат.

Если обозначить:

  • W — мировая координата точки
  • C — мировая координата центра карты
  • S — размер viewport

то итоговая позиция:

P = (W - C) +

Любое смещение карты напрямую влияет на вычисление позиции всех объектов, включая маркеры, полигоны и кастомные DOM-слои.


Преобразование LatLng в pixel space через API

В современных версиях API используется следующая последовательность:

const projection = overlay.getProjection();

const worldPoint = projection.fromLatLngToPoint(
  new google.maps.LatLng(lat, lng)
);

const scale = Math.pow(2, map.getZoom());

const pixel = {
  x: worldPoint.x * scale,
  y: worldPoint.y * scale
};

При этом важно учитывать, что getProjection() становится доступным только после вызова onAdd или draw внутри OverlayView.


Обратное преобразование пикселей в координаты

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

const worldPoint = {
  x: pixelX / scale,
  y: pixelY / scale
};

const latLng = projection.fromPointToLatLng(worldPoint);

Это позволяет интерпретировать координаты мыши как географические значения, что критично для интерактивных слоёв.


Погрешности и особенности численной стабильности

При высоких zoom-уровнях начинают проявляться ограничения точности чисел с плавающей запятой. Это особенно заметно при работе с:

  • большими значениями долготы
  • пересечением антимеридиана
  • высокоточным позиционированием overlay-элементов

Для уменьшения ошибок применяется локальная система координат относительно центра карты, а не абсолютные мировые координаты.


Использование кастомных overlay для точного позиционирования

DOM-оверлеи позволяют напрямую управлять пиксельным позиционированием без привязки к стандартным маркерам.

class HtmlOverlay extends google.maps.OverlayView {
  constructor(position, element) {
    super();
    this.position = position;
    this.element = element;
  }

  draw() {
    const projection = this.getProjection();
    const scale = Math.pow(2, this.getMap().getZoom());

    const point = projection.fromLatLngToDivPixel(this.position);

    this.element.style.transform =
      `translate(${point.x}px, ${point.y}px)`;
  }
}

Метод fromLatLngToDivPixel учитывает текущий viewport и возвращает готовые координаты для DOM.


Связь тайлов и пиксельной сетки

Каждый тайл размером 256×256 пикселей является базовой единицей отображения. При zoom z карта представляет собой сетку:

= 2^z

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


Применение в рендеринге пользовательских слоёв

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

  • тепловые карты (heatmap overlays)
  • кластеры маркеров
  • анимационные слои
  • кастомные SVG/Canvas рендеры

В таких системах производительность напрямую зависит от частоты пересчёта projection и оптимизации переходов между world space и screen space.


Оптимизация пересчётов координат

Для уменьшения нагрузки при частых изменениях карты применяется:

  • кеширование worldPoint
  • перерасчёт только при zoom_changed и center_changed
  • использование относительных координат внутри viewport

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