Клуб API Карт

Вывод части объектов из списка YMapsML

Пост в архиве.

Здравствуйте.

Передо мной стоит такая задача: через YMapsML загружается массив точек (порядка 200-300) в специальном оформлении (шаблоны значка, балуна), который нужно вывести с разбиением на группы по 10 точек с навигацией по группам. Структура XML-файла такова, что стиль назначается не каждой точке в отдельности, а всей группе ymaps:GeoObjectCollection целиком.

Сейчас я делаю так: при успешной загрузке данных из YMapsML все точки, содержащиеся в нем, простым копированием помещаются в специальный массив. При выводе заданной группы выполняются следующие действия:

1. на карту добавляется слой, содержащий в себе YMaps.ObjectManager (если он
не был еще добавлен, в противном случае имеющийся слой очищается);
2. определяется начальный и конечный индекс точек в массиве;
3. попавшие в требуемый диапазон объекты добавляются в YMaps.ObjectManager, а их описание дополнительно выводится на странице в виде простого списка;

При клике на описание точки в списке, на карте должен открываться соответствующий балун. Одно из основных назначений ObjectManager-а в том, чтобы скрывать точки, не попавшие в текущую область видимости, поэтому когда я пытаюсь программно открыть балун у таких точек, на карте ничего не происходит. В качестве решения проблемы я пробовал использовать YMaps.GeoObjectCollection, но выяснилось, что при добавлении в него объектов их стили не применяются, в результате чего на карте появляются дефолтные синие маркеры.

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

Есть ли какое-либо другое решение?

8 комментариев
*хрустит попкорном*
На сколько я понял, Вас интересует ответ на два вопроса:
1. Как добавить в диспетчер объектов метку со стилем, унаследованным от группы?
2. Как открыть балун к метки, скрытой диспетчером объектов?

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

Однако, если обойтись без промежуточного массива и добавлять метки в диспетчер объектов при проходе YMapsML-документа, то никаких дополнительных действий проводить не требуется.
var objManager = new YMaps.ObjectManager();
ml.get(0).forEach(function (obj) {
    objManager.add(obj);
});
map.addOverlay(objManager);

Для открытия балуна для невидимых объектов, необходимо будет смещать карту, например, с помощью метода panTo() и только после этого открывать балун.

Результирующий код будет таким:
// Создание обработчика для события window.onLoad
YMaps.jQuery(function () {

    // Создание экземпляра карты и его привязка к созданному контейнеру
    var map = new YMaps.Map(YMaps.jQuery("#YMapsID")[0]);
    map.setCenter(new YMaps.GeoPoint(37.609218, 55.753559), 13);

    // Создание YMapsML-документа
    var ml = new YMaps.YMapsML("http://api.yandex.ru/maps/ymapsml/examples/xml/parentstyleobject.xml");

    // Обработчик успешного завершения загрузки YMapsML-документа
    YMaps.Events.observe(ml, ml.Events.Load, function (ml) {

        // Создание диспетчера объектов
        var objManager = new YMaps.ObjectManager();

        // Обход всех элементов YMapsML-документа
        ml.get(0).forEach(function (obj) {

            // Создание ссылки на метку
            YMaps.jQuery('
  • ' + obj.name + '
  • ')
                    .find("a")

                        // При щелчке по ссылке производим необходимые действия
                        .bind("click", function () {
                            // Смещаем карту
                            map.panTo(obj.getGeoPoint(), {

                                // После окончания перемещения карты
                                callback : function () {
                                    setTimeout(function () {
                                        // Открываем балун
                                        obj.openBalloon();
                                    }, 1)
                                }
                            });
                            return false;
                        })
                    .end()

                    // Добавляем новый пункт меню в список
                    .appendTo(YMaps.jQuery("#menu"));

                // Добавляем метку в диспетчер объектов
                objManager.add(obj);
            });

            // Добавляем диспетчер объектов на карту
            map.addOverlay(objManager);
        });
    });

    Подразумевается, что контейнеры для меню и карты уже созданы и доступны через DOM:

      В диспетчер объектов (ObjectManager) метка добавляется сразу со стилями, а вот при добавлении в GeoObjectCollection стили пропадают.
      Вариант с использованием getComputedStyle() при добавлении в GeoObjectCollection работает, но с одной оговоркой:
      При добавлении объекта в коллекцию собственный стиль объекта обнуляется (или приводится к стилю всей коллекции - я не проверял).
      В справке этот момент не описан, поэтому объяснение даю наугад.

      Пример:
      // массив с объектами из YMapsML
      var MapObjects = Array();

      // Слой с объектами, добавленный на карту
      var collection = new YMaps.GeoObjectCollection();
      Map.addOverlay(collection);

      Стиль обнуляется, если делать так:
      for (var i=0; i    collection.add(MapObjects[i]);
          MapObjects[i].setStyle(style);
          collection.get(collection.length() - 1).setStyle(style);
      }

      С прямым переопределением стиля все работает корректно:
      for (var i=0; i    var style = MapObjects[i].getComputedStyle();
          collection.add(MapObjects[i]);
          MapObjects[i].setStyle(style);
          collection.get(collection.length() - 1).setStyle(style);
      }
      Я все сделал вторым способом, и сейчас все работает как надо.
      Спасибо за помощь :)

      Простите, в предыдущем примере ошибка вышла. Должно было быть так:

      Стиль обнуляется, если делать так:
      for (var i=0; i    collection.add(MapObjects[i]);
          collection.get(-1).setStyle(MapObjects[i].getComputedStyle());
      }
      Теоритически объект можно добавить в несколько групп, но стиль он будет наследовать из последней.
      // группа для отображения меток
      var group = new YMaps.GeoObjectCollection();

      var ymapml = new YMaps.YMapsML("url ваше7го документа ");
      YMaps.Events.observe(ymapml, ymapml.Events.Load, function () {
          // копируем группе для отображения стиль корневой группы ymapsml -окумента, как я понял именно ей вы задаете стиль
          group.setStyle(this.get(0).getStyle())
          // теперь можно копировать объекты из одной группы в другую с сохранением стиля
          ...
      })
      Спасибо за вариант, но именно такое решение мне не подходит, потому что в моем XML-файле могут быть вложенные группы со своими стилями, и при преобразовании такого дерева групп в плоский список я не всегда получу однородный по стилю набор элементов.
      Указывая на способ задания стиля я имел ввиду то, что у объектов свойство "style" изначально не заполнено.

      Приведенное мной выше решение хоть и уступает (теоретически) вашему в производительности, но подходит мне больше всего.
      Вы правильно выбрали, если у вас стили рапределены по дереву групп, то computedStyle самый простой способ.