Инспекция объектов карты

В JavaScript API платформы HERE Technologies вся карта строится вокруг иерархии объектов H.map.Object. Любой элемент, отображаемый на карте, наследуется от базового класса:

  • H.map.Marker — точечные объекты
  • H.map.Polyline — линии маршрутов и контуров
  • H.map.Polygon — полигоны областей
  • H.map.Group — контейнеры для группировки объектов
  • кастомные H.map.DomMarker или H.map.Rect в зависимости от сценария

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

Каждый объект хранит:

  • геометрию (getGeometry)
  • визуальные атрибуты (style, icon, color)
  • пользовательские свойства (setData / getData)
  • состояние интерактивности

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

Интерактивная работа с объектами начинается с событий карты. Основные события:

  • tap — клик или касание
  • pointerdown / pointerup — начало и конец взаимодействия
  • pointermove — перемещение курсора
  • longpress — длительное нажатие

Типичный обработчик события:

map.addEventListener('tap', function (evt) {
    const target = evt.target;
    console.log(target);
});

Если пользователь нажимает на объект, evt.target содержит ссылку на H.map.Object. Если клик пришёлся по пустой области карты, evt.target будет null.

Дополнительно доступны координаты:

map.addEventListener('tap', function (evt) {
    const x = evt.currentPointer.viewportX;
    const y = evt.currentPointer.viewportY;
});

Эти координаты используются для более глубокой инспекции через hit-testing.


Hit-testing: определение объектов под курсором

Hit-testing — ключевой механизм, позволяющий определить объекты в конкретной точке экрана.

Получение объектов через map.getObjectsAt

map.addEventListener('pointermove', function (evt) {
    const viewportX = evt.currentPointer.viewportX;
    const viewportY = evt.currentPointer.viewportY;

    const objects = map.getObjectsAt(viewportX, viewportY);

    console.log(objects);
});

Метод возвращает массив объектов, расположенных под указанной точкой. Это позволяет:

  • реализовать hover-подсветку
  • отображать tooltips
  • выделять кластеры
  • переключать курсор

Особенности поведения

  • порядок объектов зависит от z-index
  • учитываются только интерактивные объекты
  • группы (H.map.Group) разворачиваются в дочерние элементы

Инспекция маркеров (H.map.Marker)

Маркер — наиболее частый объект для инспекции.

const marker = new H.map.Marker({ lat: 52.5, lng: 13.4 });
marker.setData({
    id: 1,
    title: 'Объект A',
    category: 'store'
});
map.addObject(marker);

Обработка клика:

map.addEventListener('tap', function (evt) {
    const obj = evt.target;

    if (obj instanceof H.map.Marker) {
        const data = obj.getData();
        console.log(data.title);
    }
});

Использование пользовательских данных

Методы:

  • setData(any)
  • getData()

Часто применяются для хранения:

  • идентификаторов сущностей
  • бизнес-метаданных
  • ссылок на API-объекты

Инспекция полигонов и полилиний

Объекты геометрии позволяют анализировать не только точку, но и форму.

map.addEventListener('tap', function (evt) {
    const obj = evt.target;

    if (obj instanceof H.map.Polygon) {
        const geometry = obj.getGeometry();
        console.log(geometry.getExterior());
    }
});

Для H.map.Polyline доступна линия координат:

if (obj instanceof H.map.Polyline) {
    const strip = obj.getGeometry().getStrip();
    console.log(strip.getPoints());
}

Практическое значение

Инспекция геометрии позволяет:

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

Работа с группами объектов (H.map.Group)

Группы используются для кластеризации и логической агрегации.

const group = new H.map.Group();

group.addObjects([
    new H.map.Marker({ lat: 52.1, lng: 13.1 }),
    new H.map.Marker({ lat: 52.2, lng: 13.2 })
]);

map.addObject(group);

Инспекция внутри группы

map.addEventListener('tap', function (evt) {
    const obj = evt.target;

    if (obj instanceof H.map.Group) {
        obj.forEach(item => {
            console.log(item.getData());
        });
    }
});

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


Фильтрация и управление слоями объектов

При большом количестве объектов важно ограничивать область инспекции.

Отключение интерактивности

marker.setInteractive(false);

Проверка состояния

if (obj.isInteractive()) {
    // объект участвует в hit-testing
}

Использование групп как слоёв

Группы часто используются как логические слои:

  • слой POI
  • слой транспорта
  • слой аналитики

Инспекция может быть ограничена:

if (obj.getId && obj.getId().startsWith('traffic')) {
    // обработка слоя трафика
}

Hover-инспекция и динамическое выделение

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

let lastObj = null;

map.addEventListener('pointermove', function (evt) {
    const objs = map.getObjectsAt(evt.currentPointer.viewportX, evt.currentPointer.viewportY);

    const obj = objs && objs[0];

    if (obj !== lastObj) {
        if (lastObj) {
            lastObj.setStyle({ strokeColor: '#000' });
        }

        if (obj) {
            obj.setStyle({ strokeColor: '#ff0000' });
        }

        lastObj = obj;
    }
});

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

  • подсвечивать активный объект
  • снижать нагрузку на UI
  • избегать постоянного перерендера всей карты

Инспекция через географические координаты

Иногда требуется обратная логика: не экран → объект, а геокоордината → объект.

const geoPoint = map.screenToGeo(
    evt.currentPointer.viewportX,
    evt.currentPointer.viewportY
);

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

const objects = map.getObjects();

objects.forEach(obj => {
    const geom = obj.getGeometry();

    if (geom.containsPoint && geom.containsPoint(geoPoint)) {
        console.log('объект найден');
    }
});

Кастомные атрибуты и расширенная модель данных

Каждый объект может хранить произвольные данные:

marker.setData({
    id: 42,
    type: 'warehouse',
    status: 'active',
    payload: {
        capacity: 1200,
        region: 'north'
    }
});

При инспекции эти данные становятся ключевым источником логики:

map.addEventListener('tap', function (evt) {
    const obj = evt.target;

    const data = obj && obj.getData();

    if (data && data.type === 'warehouse') {
        console.log(data.payload.capacity);
    }
});

Инспекция кластера объектов

При использовании кластеризации (например, через группы или кастомные алгоритмы) объект на карте может представлять не одну сущность, а набор.

map.addEventListener('tap', function (evt) {
    const obj = evt.target;

    if (obj.getData && obj.getData().cluster) {
        const items = obj.getData().items;

        items.forEach(i => console.log(i.id));
    }
});

Такая структура требует разделения:

  • визуального объекта (кластер)
  • логических сущностей (список элементов)

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

При большом количестве объектов важно учитывать нагрузку:

  • getObjectsAt следует вызывать только при необходимости
  • обработку pointermove необходимо ограничивать throttling-ом
  • группы предпочтительнее большого количества отдельных объектов

Оптимизационные практики:

let lastTime = 0;

map.addEventListener('pointermove', function (evt) {
    const now = Date.now();
    if (now - lastTime < 50) return;

    lastTime = now;

    const objs = map.getObjectsAt(evt.currentPointer.viewportX, evt.currentPointer.viewportY);
});

Модель приоритетов при выборе объекта

Если под курсором несколько объектов:

  1. DOM-объекты (DomMarker)
  2. обычные маркеры
  3. полигоны
  4. линии
  5. группы

Понимание этой иерархии важно при построении логики инспекции, чтобы корректно обрабатывать пересечения и перекрытия объектов.


Интеграция инспекции с UI-слоем

Инспекция объектов карты часто используется для управления внешними интерфейсными элементами:

  • информационные окна
  • боковые панели
  • карточки объектов
  • всплывающие подсказки
map.addEventListener('tap', function (evt) {
    const obj = evt.target;

    if (!obj) return;

    const data = obj.getData();

    updateSidebar(data);
});

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