Логирование событий карты

HERE Maps API for JavaScript предоставляет развитую модель событий, позволяющую отслеживать взаимодействие пользователя с картой, изменения состояния визуализации и внутренние обновления слоёв. Логирование этих событий используется для аналитики поведения, отладки, мониторинга производительности и построения интерактивных сценариев.

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


Базовый механизм подписки на события

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

const map = new H.Map(
  document.getElementById('mapContainer'),
  defaultLayers.vector.normal.map,
  {
    center: { lat: 52.52, lng: 13.405 },
    zoom: 10,
    pixelRatio: window.devicePixelRatio || 1
  }
);

map.addEventListener('tap', function (evt) {
  console.log('Событие tap:', evt);
});

Каждое событие передаёт объект evt, содержащий:

  • тип события
  • координаты (географические и экранные)
  • целевой объект (если применимо)
  • метаданные взаимодействия

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

События взаимодействия пользователя

Наиболее часто используемые события связаны с пользовательским вводом:

  • tap — одиночное нажатие
  • longpress — длительное нажатие
  • dragstart — начало перетаскивания
  • drag — процесс перетаскивания
  • dragend — завершение перетаскивания
  • pointermove — движение указателя

Пример логирования всех взаимодействий:

const interactionEvents = [
  'tap',
  'longpress',
  'dragstart',
  'drag',
  'dragend',
  'pointermove'
];

interactionEvents.forEach(eventType => {
  map.addEventListener(eventType, evt => {
    console.log(`[${eventType}]`, {
      screenX: evt.currentPointer.viewportX,
      screenY: evt.currentPointer.viewportY,
      geo: map.screenToGeo(
        evt.currentPointer.viewportX,
        evt.currentPointer.viewportY
      )
    });
  });
});

События изменения состояния карты

Карты генерируют события при изменении масштаба, центра и ориентации.

Масштабирование и перемещение

  • mapviewchange — любое изменение представления
  • mapviewchangeend — завершение изменения
  • mapviewchangestart — начало изменения
map.addEventListener('mapviewchange', () => {
  const zoom = map.getZoom();
  const center = map.getCenter();

  console.log('Состояние карты:', {
    zoom,
    lat: center.lat,
    lng: center.lng
  });
});

Для логирования завершённых изменений используется более стабильное событие:

map.addEventListener('mapviewchangeend', () => {
  console.log('Финальное состояние:', {
    zoom: map.getZoom(),
    center: map.getCenter()
  });
});

Логирование геометрических объектов

Маркерные и векторные объекты также поддерживают события. Это важно для построения интерактивных слоёв.

const marker = new H.map.Marker({ lat: 52.5, lng: 13.4 });

marker.addEventListener('tap', evt => {
  console.log('Клик по маркеру:', evt.target.getGeometry());
});

map.addObject(marker);

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

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

group.addEventListener('tap', evt => {
  console.log('Событие группы:', evt.target);
}, true);

map.addObject(group);

Централизованная система логирования событий

При масштабных проектах целесообразно выносить обработку событий в единый логгер.

class MapEventLogger {
  constructor(mapInstance) {
    this.map = mapInstance;
    this.logs = [];

    this.attach();
  }

  attach() {
    const events = ['tap', 'drag', 'mapviewchangeend'];

    events.forEach(type => {
      this.map.addEventListener(type, evt => this.handle(type, evt));
    });
  }

  handle(type, evt) {
    const entry = {
      type,
      time: Date.now(),
      zoom: this.map.getZoom(),
      center: this.map.getCenter(),
      raw: evt
    };

    this.logs.push(entry);
    console.log('LOG:', entry);
  }

  export() {
    return JSON.stringify(this.logs);
  }
}

const logger = new MapEventLogger(map);

Фильтрация и оптимизация логирования

События pointermove и drag генерируются с высокой частотой. Без оптимизации логирование может привести к перегрузке.

Throttle-подход

function throttle(fn, delay) {
  let lastCall = 0;

  return function (...args) {
    const now = Date.now();
    if (now - lastCall >= delay) {
      lastCall = now;
      fn.apply(this, args);
    }
  };
}

map.addEventListener(
  'pointermove',
  throttle(evt => {
    console.log('Позиция:', evt.currentPointer);
  }, 100)
);

Преобразование экранных координат в географические

Для корректного логирования часто требуется сопоставление пиксельных координат с географическими:

map.addEventListener('tap', evt => {
  const geo = map.screenToGeo(
    evt.currentPointer.viewportX,
    evt.currentPointer.viewportY
  );

  console.log('Геокоординаты клика:', geo);
});

Обратное преобразование используется при генерации UI-логов:

const screenPoint = map.geoToScreen({ lat: 52.5, lng: 13.4 });
console.log(screenPoint);

События слоя и тайлов

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

  • tileloadstart
  • tileload
  • tileerror
const provider = new H.map.provider.TileProvider({
  ...options
});

provider.addEventListener('tileload', evt => {
  console.log('Загружен тайл:', evt.tile);
});

provider.addEventListener('tileerror', evt => {
  console.log('Ошибка тайла:', evt);
});

Расширенное логирование пользовательских сценариев

События могут комбинироваться в цепочки для анализа поведения:

let interactionStart = null;

map.addEventListener('dragstart', () => {
  interactionStart = Date.now();
});

map.addEventListener('dragend', () => {
  const duration = Date.now() - interactionStart;

  console.log('Длительность перетаскивания:', duration);
});

Интеграция с внешними системами логирования

Логи могут отправляться на сервер аналитики:

function sendLog(data) {
  fetch('/api/map-logs', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  });
}

map.addEventListener('mapviewchangeend', () => {
  sendLog({
    zoom: map.getZoom(),
    center: map.getCenter(),
    timestamp: Date.now()
  });
});

Обработка ошибок и событий отказа

События ошибок важны для диагностики:

map.addEventListener('error', evt => {
  console.error('Ошибка карты:', evt);
});

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


Системное логирование через перехват всех событий

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

const allEvents = [
  'tap',
  'longpress',
  'dragstart',
  'drag',
  'dragend',
  'mapviewchange',
  'mapviewchangeend',
  'pointermove'
];

allEvents.forEach(type => {
  map.addEventListener(type, evt => {
    console.log('EVENT:', {
      type,
      time: performance.now(),
      target: evt.target || null
    });
  });
});

Поведение событий в контексте слоя карты

События распространяются по иерархии:

  1. Геометрические объекты
  2. Слои
  3. Карта

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