Custom Overlays

Стандартные объекты Google Maps JavaScript API, такие как маркеры, информационные окна, полигоны и полилинии, покрывают большинство типовых задач. Однако при разработке сложных картографических интерфейсов часто возникает необходимость отображать собственные HTML-элементы, изображения, интерактивные панели, диаграммы, визуализации данных или полностью кастомные графические объекты поверх карты.

Для таких задач используется механизм Custom Overlays, основанный на классе OverlayView.

OverlayView предоставляет доступ к внутренним механизмам отрисовки карты и позволяет:

  • размещать произвольный HTML поверх карты;
  • привязывать элементы к географическим координатам;
  • контролировать жизненный цикл отображения;
  • реагировать на масштабирование и перемещение карты;
  • создавать собственные типы визуальных компонентов.

Custom Overlay является мостом между географическими координатами и DOM-элементами браузера.


Архитектура OverlayView

Все пользовательские оверлеи наследуются от класса:

google.maps.OverlayView

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

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

Каждый из этих методов выполняет определённую задачу.

Метод Назначение
onAdd() Добавление DOM-элементов на карту
draw() Позиционирование и обновление
onRemove() Очистка ресурсов

Создание собственного Overlay

Минимальная структура выглядит следующим образом:

class CustomOverlay extends google.maps.OverlayView {

    constructor(bounds, image) {
        super();

        this.bounds = bounds;
        this.image = image;
        this.div = null;
    }

    onAdd() {

    }

    draw() {

    }

    onRemove() {

    }
}

В конструкторе обычно сохраняются:

  • координаты размещения;
  • ссылки на данные;
  • параметры отображения;
  • DOM-элементы.

Метод onAdd()

Метод вызывается автоматически после добавления оверлея на карту.

Именно здесь создаются HTML-элементы.

Пример:

onAdd() {

    const div = document.createElement("div");

    div.style.position = "absolute";

    const img = document.createElement("img");

    img.src = this.image;
    img.style.width = "100%";
    img.style.height = "100%";

    div.appendChild(img);

    this.div = div;

    const panes = this.getPanes();

    panes.overlayLayer.appendChild(div);
}

Ключевым моментом является использование метода:

this.getPanes()

Он предоставляет доступ к слоям карты.


Панели карты (Map Panes)

Google Maps содержит несколько слоёв отображения.

Получить их можно так:

const panes = this.getPanes();

Доступны следующие панели:

Панель Назначение
mapPane Базовый слой карты
overlayLayer Оверлеи без событий
markerLayer Слой маркеров
overlayMouseTarget Оверлеи с поддержкой событий мыши
floatPane Информационные окна

Пример размещения интерактивного элемента:

panes.overlayMouseTarget.appendChild(div);

Такой элемент сможет получать клики.


Метод draw()

Метод отвечает за вычисление позиции оверлея на экране.

Вызывается при:

  • изменении масштаба;
  • перемещении карты;
  • изменении размеров контейнера;
  • добавлении объекта.

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

Получение объекта проекции:

const projection = this.getProjection();

Преобразование координат:

projection.fromLatLngToDivPixel()

Пример вычисления позиции

Пусть имеется точка:

const position = new google.maps.LatLng(
    55.751244,
    37.618423
);

Получение экранных координат:

const pixel = projection.fromLatLngToDivPixel(position);

Результат:

{
    x: 428,
    y: 315
}

После этого можно позиционировать элемент:

this.div.style.left = `${pixel.x}px`;
this.div.style.top = `${pixel.y}px`;

Отображение изображения внутри области

Часто Overlay используется для размещения изображений поверх определённого участка карты.

Например:

draw() {

    const projection = this.getProjection();

    const sw = projection.fromLatLngToDivPixel(
        this.bounds.getSouthWest()
    );

    const ne = projection.fromLatLngToDivPixel(
        this.bounds.getNorthEast()
    );

    const div = this.div;

    div.style.left = `${sw.x}px`;
    div.style.top = `${ne.y}px`;

    div.style.width = `${ne.x - sw.x}px`;
    div.style.height = `${sw.y - ne.y}px`;
}

Так изображение автоматически масштабируется вместе с картой.


Метод onRemove()

Вызывается при удалении оверлея.

Здесь освобождаются ресурсы и удаляются DOM-элементы.

Пример:

onRemove() {

    if (this.div) {
        this.div.parentNode.removeChild(this.div);
        this.div = null;
    }
}

Игнорирование очистки может приводить к утечкам памяти.


Добавление Overlay на карту

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

const overlay = new CustomOverlay(
    bounds,
    imageUrl
);

overlay.setMap(map);

Метод:

setMap()

запускает жизненный цикл объекта.

Последовательность вызовов:

setMap()
    ↓
onAdd()
    ↓
draw()

Удаление Overlay

Удаление выполняется через тот же метод.

overlay.setMap(null);

После этого вызывается:

onRemove()

DOM-элементы уничтожаются.


Работа с проекциями карты

Один из важнейших механизмов OverlayView — объект Projection.

Получение:

const projection =
    this.getProjection();

Основные методы:

fromLatLngToDivPixel()
fromDivPixelToLatLng()

Первый переводит координаты в пиксели.

Второй выполняет обратное преобразование.


Пример обратного преобразования

const latLng =
    projection.fromDivPixelToLatLng(
        new google.maps.Point(300, 200)
    );

Результатом станет объект:

LatLng

Это полезно при создании редакторов карт и инструментов рисования.


Создание HTML Overlay

Одним из самых распространённых сценариев является вывод собственного HTML.

class HtmlOverlay extends google.maps.OverlayView {

    constructor(position, html) {
        super();

        this.position = position;
        this.html = html;
    }

    onAdd() {

        this.div = document.createElement("div");

        this.div.innerHTML = this.html;

        this.getPanes()
            .overlayMouseTarget
            .appendChild(this.div);
    }

    draw() {

        const projection =
            this.getProjection();

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

        this.div.style.position =
            "absolute";

        this.div.style.left =
            point.x + "px";

        this.div.style.top =
            point.y + "px";
    }

    onRemove() {
        this.div.remove();
    }
}

Создание пользовательских карточек

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

Пример HTML:

<div class="store-card">
    <h3>Магазин</h3>
    <p>Режим работы: 09:00–21:00</p>
</div>

CSS:

.store-card {
    background: white;
    padding: 12px;
    border-radius: 8px;
    box-shadow: 0 2px 10px rgba(0,0,0,.2);
}

Такие карточки могут содержать:

  • кнопки;
  • формы;
  • изображения;
  • таблицы;
  • графики.

Добавление событий

Overlay способен обрабатывать стандартные DOM-события.

Пример:

this.div.addEventListener(
    "click",
    () => {
        console.log("Клик");
    }
);

Также доступны:

mouseover
mouseout
mousedown
mouseup
dblclick
contextmenu

Блокировка событий карты

Иногда необходимо предотвратить обработку событий картой.

Например:

this.div.addEventListener(
    "click",
    event => {
        event.stopPropagation();
    }
);

Это предотвращает запуск событий карты после клика по Overlay.


Анимация Overlay

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

Пример:

.marker {
    animation: pulse 1s infinite;
}

@keyframes pulse {

    0% {
        transform: scale(1);
    }

    50% {
        transform: scale(1.3);
    }

    100% {
        transform: scale(1);
    }
}

Использование Canvas внутри Overlay

Для отображения большого количества объектов часто используется Canvas.

Создание:

const canvas =
    document.createElement("canvas");

Добавление:

this.div.appendChild(canvas);

Далее можно использовать стандартный API Canvas:

const ctx = canvas.getContext("2d");

ctx.beginPath();

ctx.arc(
    100,
    100,
    30,
    0,
    Math.PI * 2
);

ctx.fill();

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

SVG особенно полезен для:

  • диаграмм;
  • тепловых карт;
  • маршрутов;
  • интерактивной графики.

Пример:

this.div.innerHTML = `
<svg width="100" height="100">
    <circle
        cx="50"
        cy="50"
        r="40"
    />
</svg>
`;

Overlay как замена Marker

Для полной кастомизации маркеров Overlay часто оказывается удобнее стандартного Marker.

Пример:

<div class="custom-marker">
    ★
</div>
.custom-marker {
    font-size: 28px;
    color: red;
}

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

  • произвольный HTML;
  • CSS-анимации;
  • встроенные события;
  • сложные интерфейсы.

Производительность при большом количестве Overlay

Каждый Overlay создаёт DOM-узел.

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

  • рост памяти;
  • увеличение времени рендеринга;
  • медленная прокрутка карты.

Плохой вариант:

5000 Overlay объектов

Лучший вариант:

1 Overlay + Canvas

или

кластеризация

Скрытие Overlay

Для временного скрытия:

this.div.style.display = "none";

Показ:

this.div.style.display = "block";

При этом объект остаётся привязанным к карте.


Изменение прозрачности

this.div.style.opacity = "0.5";

Либо через CSS:

.overlay {
    opacity: 0.5;
}

Управление порядком слоёв

Используется свойство:

z-index

Пример:

this.div.style.zIndex = 1000;

Большие значения отображаются поверх остальных объектов.


Реализация метода hide()

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

hide() {

    if (this.div) {
        this.div.style.visibility =
            "hidden";
    }
}

Реализация метода show()

show() {

    if (this.div) {
        this.div.style.visibility =
            "visible";
    }
}

Реализация метода toggle()

toggle() {

    if (
        this.div.style.visibility ===
        "hidden"
    ) {

        this.show();

    } else {

        this.hide();
    }
}

Реализация метода toggleDOM()

Полное удаление и восстановление объекта:

toggleDOM() {

    if (this.getMap()) {

        this.setMap(null);

    } else {

        this.setMap(map);
    }
}

Такой подход позволяет полностью исключать Overlay из процесса рендеринга.


Практические сценарии применения

Геопривязанные изображения

Использование исторических карт, спутниковых снимков или планов зданий поверх основной карты.

Интерактивные карточки объектов

Отображение информации о магазинах, складах, объектах недвижимости и транспортных узлах.

Тепловые карты собственного формата

Отрисовка аналитических данных через Canvas или SVG.

Визуализация IoT-данных

Отображение показаний датчиков, телеметрии и мониторинга оборудования.

Дашборды поверх карты

Размещение диаграмм, счётчиков и индикаторов непосредственно в географическом контексте.

Сложные пользовательские маркеры

Создание маркеров с несколькими уровнями вложенности, анимациями и динамическим содержимым.

Редакторы геоданных

Построение интерфейсов для рисования, перемещения и редактирования пространственных объектов с использованием обратного преобразования координат через объект Projection.

Custom Overlays являются одним из самых мощных механизмов Google Maps JavaScript API, позволяя интегрировать произвольный HTML, Canvas и SVG в пространство карты, полностью контролируя процесс отрисовки, позиционирования, взаимодействия с пользователем и управление жизненным циклом отображаемых объектов.