Использование собственных событий в OpenLayers

Материал из GIS-Lab
Перейти к навигации Перейти к поиску
Эта страница является черновиком статьи.


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

Предположим, перед нами стоит следующая задача: имеется векторный слой (vectors) и некоторое множество объектов (feature) класса OpenLayers.Feature.Vector на нём. Необходимо при изменении расположения (feature.geometry), стиля (feature.style) или названия (feature.attributes.title) сериализовать соответствующие свойства в JSON-объект и передать на сервер. Другими словами, мы хотим сохранять данные непосредственно в процессе их изменения, не создавая дополнительных инструментов типа "Сохранить данные".

В принципе нас не интересуют детали того, как именно эти свойства будут изменяться (с помощью каких-либо контролов или в ручную или еще как-нибудь), для нас важно научиться отслеживать и должным образом обрабатывать эти события.

По умолчанию при изменении геометрии в OpenLayers происходит срабатывание стандартного события "featuremodified", нативных же событий, срабатывающих при изменении стиля или какого-нибудь из свойств объекта feature.attributes не предусмотрено.

Придумаем названия для события изменения стиля - "stylemodified", изменения аттрибута title - "titlemodified". Переходим к регистрации событий (небольшая справка по использованию событий в OpenLayers доступна здесь).

vectors.events.register("featuremodified", null, update)
vectors.events.register("stylemodified", null, update)
vectors.events.register("titlemodified", null, update)

В данном примере update - это callback-функция, вызываемая при срабатывании события. Обратите внимание на то, что во всех трёх случаях используется одна и та же callback-функция. Конечно, можно было для каждого события написать свой обработчик, но так как фактически всё время будет выполнятся одна и та же задача - передача данных на сервер с разницей лишь в самих данных, то в таком случае код, отвечающий за передачу информации на сервер дублировался бы трижды.

Так как "featuremodified" - стандартное событие, то вызывать нам вручную его не требуется, соответствующие вызовы уже расставлены внутри кода OpenLayers. Вызывать же события "stylemodified" и "titlemodified" придётся вручную.

Предположим, что за изменение стиля отвечает некоторая функция changeStyle, а за изменение названия changeTitle. Тогда вызовы соответствующих событий должны быть добавлены в конец этих функций:

function changeStyle(feature) {
    ...
    feature.layer.events.triggerEvent("stylemodified", {feature: feature});
}

function changeTitle(feature) {
    ...
    feature.layer.events.triggerEvent("titlemodified", {feature: feature});
}

Второй аргумент функции triggerEvent - объект, который будет передаваться в callback-функцию. Аналогичным образом стандартное событие "featuremodified" передаёт в callback объект, свойство feature которого - есть ссылка на модифицированный объект, подробнее здесь.

Как же теперь внутри callback-а update определить от какого именно из событий пришли данные, чтобы соответствующим образом сформировать строку, которую нужно передать на сервер, ведь мы вроде-бы подаём на вход только объект {feature: feature} и он не содержит в себе имени типа события. Однако, если залезть в исходники OpenLayers и найти определение функции triggerEvent, то можно увидеть следующее:

triggerEvent: function (type, evt) {
    ...
    if(!evt.type) {
        evt.type = type;
    }
    ...
}

То есть в объект (evt), подающийся на вход callback-у добавляется свойство type, совпадающее с именем события.

Используя полученные знания, напишем callback-функцию update:

json = new OpenLayers.Format.JSON();
geojson = new OpenLayers.Format.GeoJSON();

function update(evt) {
    switch (evt.type) {
        case "featuremodified":
            data = '{"geom":'+geojson.write(evt.feature.geometry)+'}';
            break;
        case "stylemodified":
            data = '{"style":'+json.write(evt.feature.style)+'}';
            break;
        case "titlemodified":
            data = '{"title":'+json.write(evt.feature.attributes.title)+'}';
            break;
    }
    OpenLayers.Request.PUT({
        url: "your_url/" + evt.feature.attributes.id,
        data: data
    });
}

Вот и всё, ничего сложного.

Дополнение

Не возникло ли у вас вопроса, почему у функции register второй аргумент равен null и для чего он в принципе может использоваться? Попробуем разобраться. Второй аргумент в терминах JavaScript - это scope, в контексте которого будет вызываться callback-функция обработчик.

Поясним на примере:

vectors.events.register("featuremodified", {"type": "geometry"}, update)

То есть при вызове функции update её this будет ссылаться на объект:

{"type": "geometry"}

В этом случае update будет выглядеть следующим образом:

function update(evt) {
    switch (this.type) {
        case "geometry":
            data = '{"geom":'+geojson.write(evt.feature.geometry)+'}';
            break;
      case "style":
          data = '{"style":'+json.write(evt.feature.style)+'}';
          break;
      case "title":
          data = '{"title":'+json.write(evt.feature.attributes.title)+'}';
          break;
    }
    OpenLayers.Request.PUT({
        url: "your_url/" + evt.feature.attributes.id,
        data: data
    });
}

А функции изменения стиля и названия:

function changeStyle(feature) {
    ...
    update.call({"type": "style"}, {feature: feature})
}

function changeTitle(feature) {
    ...
    update.call({"type": "title"}, {feature: feature})
}

Метод сall - стандартная возможность JavaScript, позволяющая вызывать функции в контексте нужного объекта.