Клуб API Карт

Кластеры + YMapsML с несколькими xml

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

Добрый день!

Есть xml c объектами, которые кластеризуются и отображаются на карте, здесь вроде вопросов нет:

        ymaps.geoXml.load("file.xml").then(function (res) {
                clusterer = new ymaps.Clusterer({margin:[20]});
                res.geoObjects.each(function (obj, objIndex, group) {
                  obj.properties.set('clusterCaption', $i++);
                  clusterer.add(obj);
                });
               myMap.geoObjects.add(clusterer);
        }, function (error) {
            alert('Ошибка: ' + error);
        });

 

Затруднения возникли при усложнении задачи: есть два xml, объекты которых хочется показывать попеременно, переключаясь с помощью кнопок (ymaps.control.RadioGroup). Пока нашел только одно некрасивое, но работающее решение: по нажатию на кнопку делаю clusterer.removeAll() и подгружаю нужный xml. Но объектов больше тысячи, и это решение работает медленно. Посоветуйте, пожалуйста, более оптимальное решение. Спасибо!

10 комментариев

В этой задаче я вижу два узких места:

1. Постоянная загрузка того или иного xml

2. Добавление-удаление объектов из кластеризатора.

 

Теперь решения:

1. Вы можете загружать оба xml-файла по 1 разу и просто хранить сформированный на их основе массив, чтобы при переключении не делать новую загрузку.

2. Вы можете создать 2 разных кластеризатора, каждый для отдельного файла. И тогда при переключении между данными у вас не будет уходить время на их предобработку. Вы будете просто удалять или добавлять соответствующий кластеризатор на карту.

 

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

Александр И.
28 января 2016, 03:21

Спасибо за ответ и за совет по 2.0.18. Ускорение этой версии почти убило желание оптимизировать, настолько шустрее все стало работать (если интересно: http://drevo-info.ru/map.html).

К сожалению, я плохо знаю js, поэтому попытка создания двух кластереров одной функцией, загружающей xml, не получилась. Ниже я пытаюсь в init загрузить оба xml и отобразить один из них (пока без обработки кнопок): 

        function loadxml(xml){ 

            $i=1;

            clusterer = new ymaps.Clusterer({margin:[20]});

            ymaps.geoXml.load("http://drevo-info.ru/"+xml)

            .then(function (res) { // функция обрабатывает успешный результат получения YMapsML        

                res.geoObjects.each(function (obj, objIndex, group) {

                    obj.properties.set('clusterCaption', $i++);

                    clusterer.add(obj);

                });

            }, function (error) { // Вызывается в случае неудачной загрузки YMapsML

                alert('Ошибка: ' + error);

            });

            return clusterer;

        }

        clustgeo = loadxml("mapgeo.xml");

        clust = loadxml("map.xml");

        myMap.geoObjects.add(clustgeo); 

 

В результате на карте не отображается ни один объект. Буду благодарен, если укажете на ошибку или дадите рабочий пример.

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

В этом варианте ошибка, посмотрите ответ ниже =)

 

Сделайте, чтобы функция loadxml возвращала не кластеризатор, а массив геообъектов. Потом создайте 2 кластера, и в них добавьте полученные массивы.

function loadxml(xml){ 

            $i=1;

            var result = [];

            ymaps.geoXml.load("http://drevo-info.ru/"+xml)

            .then(function (res) { // функция обрабатывает успешный результат получения YMapsML        

                res.geoObjects.each(function (obj, objIndex, group) {

                    obj.properties.set('clusterCaption', $i++);

                    result.push(obj);

                });

            }, function (error) { // Вызывается в случае неудачной загрузки YMapsML

                alert('Ошибка: ' + error);

            });

            return result;

        }

var clusterer1 = new ymaps.Clusterer(),

     clusterer2 = new ymaps.Clusterer();

clusterer1.add(loadxml('mapgeo.xml'));

clusterer2.add(loadxml( "map.xml" );

Переключаться просто:

myMap.geoObjects.remove(clusterer1);

myMap.geoObjects.add(clusterer2);

Невнимательно посмотрела и не учла, что запрос асинхронный. Вот исправленный вариант.

function loadxml(xml, clusterer){ 

            ymaps.geoXml.load("http://drevo-info.ru/"+xml)

            .then(function (res) { // функция обрабатывает успешный результат получения YMapsML        

                 var result = [],

                        i=1;

                res.geoObjects.each(function (obj, objIndex, group) {

                    obj.properties.set('clusterCaption', i++);

                    result.push(obj);

                });

               clusterer.add(result);

            }, function (error) { // Вызывается в случае неудачной загрузки YMapsML

                alert('Ошибка: ' + error);

            });

        }

var clusterer1 = new ymaps.Clusterer(),

     clusterer2 = new ymaps.Clusterer();

loadxml('mapgeo.xml', clusterer1);

loadxml( "map.xml", clusterer2);

 

Александр И.
28 января 2016, 03:21

Огромное спасибо за помощь! Теперь со скоростью всё в порядке.

Но возник еще один вопрос: на моем сайте на карте (http://drevo-info.ru/map.html) очень странно работает выпадающий список typeSelector: в IE вроде все нормально, а в Firefox/Chrome активная область этого элемента управления находится только в районе его нижней кромки, щелчки мышью по центру элемента остаются без результата. Это баг API или проблема моего css (хотя для карт я стили не настраивал)?

У нас есть неприятная особенность у элементов управления - контейнер, в котором лежат кнопки левой части карты, растягивается на весь экран, и перекрывает частично контрол справа. Мы починим это в будущих релизах. На данный момент решить проблему можно, поменяв порядок добавления элементов на карту - сначала добавить кнопки, которые располагаются в левой части, а потом контрол типов карты.

Александр И.
28 января 2016, 03:21

Благодарю, теперь все как надо.

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

Случайно наткнулся на вашу проблему, когда искал ответ на подобную. У меня упорно не работает кластеризатор. К счастью в моем случае объектов немного и я попробовал использовать ваш отрывок кода. Он не работает. Можно взглянуть на весь ваш код, если это вас не затруднит. Т.к. я достаточно неопытен в JS (изучаю его всего месяц),  а мне уже нужно кластеризировать объекты в картах.

 

Всего наилучшего.

Заранее благодарен.

Александр И.
28 января 2016, 03:21

Вот мой рабочий вариант:

 

    ymaps.ready(init);

    var myMap, 

    myPlacemark;

 

    function init(){ 

        myMap = new ymaps.Map ("map", {

            center: [55.76, 37.64],

            behaviors: ["default", "scrollZoom"],

            zoom: 2

        }); 

        myMap.controls.add('zoomControl');

        myMap.controls.add('scaleLine');

        myMap.controls.add('miniMap');

      

        function loadxml(xml, clusterer){ 

            ymaps.geoXml.load("http://drevo-info.ru/"+xml)

            .then(function (res) { // функция обрабатывает успешный результат получения YMapsML                   var result = [],

                i=1;

                res.geoObjects.each(function (obj, objIndex, group) {

                    obj.properties.set('clusterCaption', i++);

                    result.push(obj);

                });

                clusterer.add(result);

            }, function (error) { // Вызывается в случае неудачной загрузки YMapsML

                //alert('Ошибка: ' + error.message);

            });

        }

        

        var clusterer1 = new ymaps.Clusterer({margin:[20]}),

        clusterer2 = new ymaps.Clusterer({margin:[20]}),

        clusterer3 = new ymaps.Clusterer({margin:[20]});

 

        loadxml('mapgeo.xml', clusterer1);

        loadxml("map.xml", clusterer2);

        loadxml("maporg.xml", clusterer3);

 

        myMap.geoObjects.add(clusterer2);

 

        var myRadioGroup = new ymaps.control.RadioGroup({

            items: [

                new ymaps.control.Button('Географические объекты'),

                new ymaps.control.Button('Монастыри, храмы, некрополи'),

                new ymaps.control.Button('Организации'),

                new ymaps.control.Button('Только карта'),

            ]

        });

        

        myMap.controls.add(myRadioGroup);

        myMap.controls.add('typeSelector');

 

        // Вторая кнопка будет выбрана по умолчанию.

        myRadioGroup.get(1).select();

        myRadioGroup.get(0).events.add("click", function (e) {

            myMap.geoObjects.remove(clusterer2);

            myMap.geoObjects.remove(clusterer3);

            myMap.geoObjects.add(clusterer1);

        });

        myRadioGroup.get(1).events.add("click", function (e) {      

            myMap.geoObjects.remove(clusterer1);

            myMap.geoObjects.remove(clusterer3);

            myMap.geoObjects.add(clusterer2);

        });

        myRadioGroup.get(2).events.add("click", function (e) {      

            myMap.geoObjects.remove(clusterer1);

            myMap.geoObjects.remove(clusterer2);

            myMap.geoObjects.add(clusterer3);

        });        

        myRadioGroup.get(3).events.add("click", function (e) {      

            myMap.geoObjects.remove(clusterer1);

            myMap.geoObjects.remove(clusterer2);

            myMap.geoObjects.remove(clusterer3);

        });        

    }


Не знаю, как здесь правильно обозначить код. Кажется, автосмайлики его подпортят. Если что, работающий вариант можно поглядеть в коде страницы http://drevo-info.ru/map.html

Спасибо. Вы мне очень помогли. Посмотрим что получится.