ScreenSpaceEventHandler

ScreenSpaceEventHandler — один из ключевых механизмов взаимодействия пользователя со сценой в CesiumJS. Класс предназначен для обработки событий ввода, происходящих внутри HTML-элемента, связанного с визуализацией карты или глобуса. С его помощью реализуются реакции на щелчки мыши, перемещение курсора, двойные клики, использование колеса прокрутки, касания на сенсорных устройствах и различные комбинации клавиш-модификаторов.

Практически любой интерактивный инструмент в CesiumJS — выбор объектов, отображение информации по клику, измерение расстояний, рисование геометрии, перемещение сущностей — строится на основе ScreenSpaceEventHandler.


Создание обработчика

Обычно обработчик создаётся для канваса (canvas) объекта Viewer.

const viewer = new Cesium.Viewer("cesiumContainer");

const handler = new Cesium.ScreenSpaceEventHandler(
    viewer.scene.canvas
);

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


Архитектура работы

Обработка событий строится на трёх основных компонентах:

  1. Экземпляр ScreenSpaceEventHandler.
  2. Тип события (ScreenSpaceEventType).
  3. Функция-обработчик.

Общая схема выглядит следующим образом:

handler.setInputAction(
    function(event) {
        console.log(event);
    },
    Cesium.ScreenSpaceEventType.LEFT_CLICK
);

Когда происходит событие LEFT_CLICK, вызывается переданная функция.


Перечисление ScreenSpaceEventType

Cesium предоставляет набор предопределённых типов событий.

LEFT_CLICK

Обычный щелчок левой кнопкой мыши.

handler.setInputAction(function(event) {
    console.log("Левый клик");
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

LEFT_DOUBLE_CLICK

Двойной щелчок левой кнопкой.

handler.setInputAction(function(event) {
    console.log("Двойной клик");
}, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);

По умолчанию Cesium использует двойной клик для приближения камеры к выбранной точке сцены.


LEFT_DOWN

Нажатие левой кнопки мыши.

handler.setInputAction(function(event) {
    console.log("Кнопка нажата");
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);

Событие возникает сразу после нажатия.


LEFT_UP

Отпускание левой кнопки.

handler.setInputAction(function(event) {
    console.log("Кнопка отпущена");
}, Cesium.ScreenSpaceEventType.LEFT_UP);

RIGHT_CLICK

Щелчок правой кнопкой мыши.

handler.setInputAction(function(event) {
    console.log("Правый клик");
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);

RIGHT_DOWN и RIGHT_UP

События нажатия и отпускания правой кнопки.

handler.setInputAction(function(event) {
    console.log("Правая кнопка нажата");
}, Cesium.ScreenSpaceEventType.RIGHT_DOWN);
handler.setInputAction(function(event) {
    console.log("Правая кнопка отпущена");
}, Cesium.ScreenSpaceEventType.RIGHT_UP);

MIDDLE_CLICK

Щелчок средней кнопкой мыши.

handler.setInputAction(function(event) {
    console.log("Средняя кнопка");
}, Cesium.ScreenSpaceEventType.MIDDLE_CLICK);

MIDDLE_DOWN и MIDDLE_UP

Нажатие и отпускание средней кнопки.

handler.setInputAction(function(event) {
    console.log("Средняя кнопка нажата");
}, Cesium.ScreenSpaceEventType.MIDDLE_DOWN);

MOUSE_MOVE

Перемещение курсора.

Одно из наиболее часто используемых событий.

handler.setInputAction(function(event) {
    console.log(event.endPosition);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

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

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

WHEEL

Прокрутка колеса мыши.

handler.setInputAction(function(delta) {
    console.log(delta);
}, Cesium.ScreenSpaceEventType.WHEEL);

Событие позволяет реализовывать собственную логику масштабирования или изменения параметров интерфейса.


PINCH_START

Начало жеста масштабирования на сенсорном устройстве.

handler.setInputAction(function(event) {
    console.log("Начало pinch");
}, Cesium.ScreenSpaceEventType.PINCH_START);

PINCH_MOVE

Изменение масштаба во время жеста.

handler.setInputAction(function(event) {
    console.log("Pinch move");
}, Cesium.ScreenSpaceEventType.PINCH_MOVE);

PINCH_END

Завершение жеста.

handler.setInputAction(function(event) {
    console.log("Pinch end");
}, Cesium.ScreenSpaceEventType.PINCH_END);

Объекты событий

Тип передаваемого объекта зависит от конкретного события.

PositionedEvent

Используется для одиночных кликов.

Структура:

{
    position: Cartesian2
}

Пример:

handler.setInputAction(function(event) {
    console.log(event.position.x);
    console.log(event.position.y);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

MotionEvent

Используется для движения курсора.

Структура:

{
    startPosition: Cartesian2,
    endPosition: Cartesian2
}

Пример:

handler.setInputAction(function(event) {

    console.log(event.startPosition);
    console.log(event.endPosition);

}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

TwoPointEvent

Применяется для мультитач-событий.

Структура содержит позиции двух касаний.

{
    position1,
    position2
}

Используется при работе с жестами масштабирования и вращения.


Получение координат точки клика

Одно из самых распространённых применений — определение географических координат.

Получение позиции на эллипсоиде

handler.setInputAction(function(event) {

    const cartesian =
        viewer.camera.pickEllipsoid(
            event.position
        );

    if (cartesian) {

        const cartographic =
            Cesium.Cartographic.fromCartesian(
                cartesian
            );

        const longitude =
            Cesium.Math.toDegrees(
                cartographic.longitude
            );

        const latitude =
            Cesium.Math.toDegrees(
                cartographic.latitude
            );

        console.log(latitude, longitude);
    }

}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

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


Получение координат с учётом рельефа

Если используется Terrain, рекомендуется применять pickPosition.

handler.setInputAction(function(event) {

    const cartesian =
        viewer.scene.pickPosition(
            event.position
        );

    console.log(cartesian);

}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

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


Выбор объектов сцены

Для определения объекта под курсором используется метод scene.pick().

handler.setInputAction(function(event) {

    const pickedObject =
        viewer.scene.pick(
            event.position
        );

    console.log(pickedObject);

}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

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

handler.setInputAction(function(event) {

    const picked =
        viewer.scene.pick(
            event.position
        );

    if (Cesium.defined(picked)) {

        console.log(picked.id);

    }

}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

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


Подсветка объектов при наведении

Типичный сценарий взаимодействия — выделение объекта под курсором.

let highlighted;

handler.setInputAction(function(event) {

    const picked =
        viewer.scene.pick(
            event.endPosition
        );

    if (
        Cesium.defined(picked) &&
        picked.id
    ) {

        if (
            highlighted &&
            highlighted !== picked.id
        ) {

            highlighted.point.color =
                Cesium.Color.WHITE;
        }

        highlighted = picked.id;

        highlighted.point.color =
            Cesium.Color.YELLOW;
    }

}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

Подобная логика широко применяется в геоинформационных системах.


Реализация Drag & Drop

Начало перетаскивания

let selected = null;

handler.setInputAction(function(event) {

    const picked =
        viewer.scene.pick(
            event.position
        );

    if (
        Cesium.defined(picked)
    ) {
        selected = picked.id;
    }

}, Cesium.ScreenSpaceEventType.LEFT_DOWN);

Перемещение объекта

handler.setInputAction(function(event) {

    if (!selected) {
        return;
    }

    const position =
        viewer.camera.pickEllipsoid(
            event.endPosition
        );

    if (position) {
        selected.position = position;
    }

}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

Завершение перемещения

handler.setInputAction(function() {

    selected = null;

}, Cesium.ScreenSpaceEventType.LEFT_UP);

Использование клавиш-модификаторов

Обработчики могут реагировать только при зажатых клавишах.

Доступны:

  • Shift
  • Ctrl
  • Alt

Shift + Левый клик

handler.setInputAction(
    function(event) {

        console.log("Shift + Click");

    },
    Cesium.ScreenSpaceEventType.LEFT_CLICK,
    Cesium.KeyboardEventModifier.SHIFT
);

Ctrl + Левый клик

handler.setInputAction(
    function(event) {

        console.log("Ctrl + Click");

    },
    Cesium.ScreenSpaceEventType.LEFT_CLICK,
    Cesium.KeyboardEventModifier.CTRL
);

Alt + Левый клик

handler.setInputAction(
    function(event) {

        console.log("Alt + Click");

    },
    Cesium.ScreenSpaceEventType.LEFT_CLICK,
    Cesium.KeyboardEventModifier.ALT
);

Замена существующих обработчиков

Если для одного события вызывается setInputAction() повторно, предыдущий обработчик заменяется.

handler.setInputAction(firstHandler,
    Cesium.ScreenSpaceEventType.LEFT_CLICK);

handler.setInputAction(secondHandler,
    Cesium.ScreenSpaceEventType.LEFT_CLICK);

После выполнения второго вызова будет работать только secondHandler.


Получение существующего обработчика

Для чтения зарегистрированного обработчика используется метод getInputAction().

const callback =
    handler.getInputAction(
        Cesium.ScreenSpaceEventType.LEFT_CLICK
    );

Это позволяет временно сохранять стандартную логику Cesium и затем восстанавливать её.


Удаление обработчика

Для удаления используется removeInputAction().

handler.removeInputAction(
    Cesium.ScreenSpaceEventType.LEFT_CLICK
);

После удаления событие больше не будет обрабатываться.


Освобождение ресурсов

При уничтожении инструмента или компонента обработчик рекомендуется удалять.

handler.destroy();

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

if (!handler.isDestroyed()) {
    handler.destroy();
}

После вызова destroy() экземпляр становится непригодным для дальнейшего использования.


Отключение стандартного поведения Cesium

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

Например, можно отключить масштабирование двойным кликом.

viewer.cesiumWidget.screenSpaceEventHandler
    .removeInputAction(
        Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
    );

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


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

Допускается создание нескольких экземпляров ScreenSpaceEventHandler.

const selectionHandler =
    new Cesium.ScreenSpaceEventHandler(
        viewer.scene.canvas
    );

const drawingHandler =
    new Cesium.ScreenSpaceEventHandler(
        viewer.scene.canvas
    );

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


Построение инструмента измерения расстояний

Типичный алгоритм работы через ScreenSpaceEventHandler:

  1. Левый клик добавляет точку.
  2. Перемещение мыши показывает предварительный результат.
  3. Второй клик завершает измерение.
  4. Правый клик отменяет операцию.

Пример регистрации событий:

handler.setInputAction(
    addPoint,
    Cesium.ScreenSpaceEventType.LEFT_CLICK
);

handler.setInputAction(
    updatePreview,
    Cesium.ScreenSpaceEventType.MOUSE_MOVE
);

handler.setInputAction(
    cancelMeasurement,
    Cesium.ScreenSpaceEventType.RIGHT_CLICK
);

Подобная схема лежит в основе большинства интерактивных инструментов ГИС-приложений.


Типичные ошибки

Отсутствие проверки pick()

Неверно:

const picked =
    viewer.scene.pick(
        event.position
    );

console.log(picked.id);

Правильно:

const picked =
    viewer.scene.pick(
        event.position
    );

if (Cesium.defined(picked)) {
    console.log(picked.id);
}

Использование обработчика после destroy()

Неверно:

handler.destroy();

handler.setInputAction(...);

После уничтожения объект необходимо создавать заново.


Игнорирование очистки обработчиков

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

handler.removeInputAction(
    Cesium.ScreenSpaceEventType.MOUSE_MOVE
);

или полностью уничтожать экземпляр:

handler.destroy();

Иначе возможны утечки памяти и накопление лишней логики обработки.


Практические области применения

ScreenSpaceEventHandler используется практически во всех интерактивных возможностях CesiumJS:

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

Благодаря единому механизму обработки мыши, клавиатурных модификаторов и сенсорных жестов класс ScreenSpaceEventHandler выступает центральным элементом пользовательского взаимодействия со сценой CesiumJS и является фундаментом для разработки сложных интерактивных приложений на основе трёхмерной геовизуализации.