Клуб API Карт

Оптимизация показа меток

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

Я уже создал карту, могу ее редактировать http://clubs.ya.ru/mapsapi/replies.xml?parent_id=27119&item_no=27118

 

Теперь нужно научиться грамотно показывать метки, а именно:

1) Показывать метки в определенном зуме

2) Показывать только те метки, которые входят в область видимости карты

 

По первомe вопросу, есть такое предложение, но данное событие срабатывает и при  'drag' карты,

нужно событие типа 'zoomchange'.

 

myMap.events.add('boundschange', function (e) {
if (e.get('newZoom')==2)
{
//load placemarks
}

});

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

 

19 комментариев
Алексей Yarrr!
28 января 2016, 04:05
координаты границы карты,

Дык, map.getBounds() ?

getBounds

 

{Number[][]} getBounds()

 

 

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

Евгений Белоусов
28 января 2016, 04:05

это 2 точки, а не 4-е :)

Алексей Yarrr!
28 января 2016, 04:05

а зачем 4?

(x0, y0), (x1, y1) - две точки.

левый верхний - x0 y0, правый верхний x1 y0, левый нижний x0 y1, и правый нижний x1 y1.

у нас же прямоугольник. был бы квадрат - можно было одну точку и размер передавать. еще на один скаляр меньше ;)

Алексей Yarrr!
28 января 2016, 04:05
  • oldZoom - предыдущий уровень машсштабирования;
  • newZoom - новый уровень масштабирования

Соотв., по первому вопросу if (e.get('oldZoom') != e.get('newZoom')) - значит zoomchange.

Читайте документацию, она интересная) http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/Map.xml

Спасибо,   !

Все получилось http://nadabs.org.ua/maps.php

 

Единственная проблема: поддергивание меток при 'boundschange'.

Потомучто перед добавлением, я удаляю старые метки

myCollection.removeAll();

Как избавиться от этого?

 

Алексей Yarrr!
28 января 2016, 04:05

А вы уверены, что вам их руками нужно удалять?

Я к тому, что апи2.0 стало умнее и фильтрует вывод меток само. Вполне возможно, что вам не надо постоянно чистить коллекцию и обновлять метки. Но если надо, то:

Создаете 2 коллекции, одну показываете на карте, вторую как буффер. Нужно сменить метки - заполняете вторую, вываливаете на карту, убираете первую с карты, и чистите первую, меняете их местами. Может быть так не будут дергаться, но может и нет ;-). Если меток много - то дергания неизбежны, и нужен канвас. С ним дерганий точно можно избежать.

Алексей Yarrr!
28 января 2016, 04:05

Кстати, еще момент. Метки, которые есть в первой коллекции, вы можете добавить и во вторую, т.к. это объекты и фактически в коллекцию добавляется только ссылка на конкретный экземпляр объекта.

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

Алексей Yarrr!
28 января 2016, 04:05

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

Можно на сетку поделить, загружать объекты только для видимых ячеек, и сохранять все объекты на сетке в какое-то хранилище, чтобы не дергать их аяксом. Работа по сети всегда дольше, чем напрямую с памятью (если не брать откровенно редкие странные случаи ;-).

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

А что, если просто сравнивать: метка создана то дабавлять не надо.

Но это токже может сказаться на производительности.

Алексей Yarrr!
28 января 2016, 04:05

Как бы, нет. Это один из способов оптимизировать код. Если у ваших меток есть номера, или хотя бы текстовые названия, то вы легко можете составить хэш, где в ключах будут эти номера, а в значениях любая информация о них - будь то просто bool флаг видимости (показана/скрыта) или же хэш с набором данных о метке (имя, фамилия, адрес, и т.п.) - не важно, ибо одно наличие этого ключа в хэше уже будет говорить о его существовании - значит и не надо добавлять её и тем более заного загружать. Ну и в случае, если ничего не нужно загружать, миганий вообще не будет, а в случае частичной дозагрузки - просто появится дозагруженное. И вырезать метки из кластера не нужно - попробуйте сначала просто добавлять их. 100 метод для кластеризатора - мелочи, он расчитан на тысячи.

Как перебрать коллекцию? Этот способ не работает

myCollection.each(function (geoObject) {
alert(geoObject.properties.get('id_place'))  
});

 

 

Алексей Yarrr!
28 января 2016, 04:05

var iterator = myGroup.getIterator(), object;

while (object = iterator.getNext()) { ... }

 

вообще, и myCollection.each должен работать. callback пробовали задавать, как function (key, geoObject) ?

через getIterator() работает

Вот, что у меня получилось

var iterator = myCollection.getIterator(),
    object,
    check=0;

while (object = iterator.getNext()) {
    if (this.id_place==object.properties.get('id_place')){
    var check=1;
    }
}

if (check!=1)
{
myCollection.add(myPlacemark);
}



Алексей Yarrr!
28 января 2016, 04:05

Ну да, не совсем то, что я предлагал.)

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

Сделайте check видимым глобальным для вашего скоупа. Сделайте его видимым внутри всех ваших алгоритмов. примерно так:

(function(){

  var check = {};

  ymaps.ready(init);

  function init(){

    // ... делаем что-то, запускаем загрузку

  };

...

    // где-то в колбеке загрузки объектов

    {for each loaded objects}

      if (!check[object.id_place]) {

        check[object.id_place] = object.id_place

        check[object.id_place].placemark = new ymaps.GeoObject(...); // сохраняем всю метку 

        myCollection.add(check[obj.id_place].placemark);

      }

      ...

    {/for}

...

});

 

т.е. сохраняйте исходный объект в хэш по его id и туда же кладите ссылку на метку - будет проще в будущем её искать. используя хэш - не надо перебирать все метки в поиске загруженной - экономите на CPU, будет отображаться быстрее. коллекция перерисовывается каждые сколько-то ms, поэтому не надо руками запускать перерисовку - меньше кода. и если позже придется искать метку или удалять её - это можно будет легко сделать по id: у вас есть ссылка на объект коллекции, и есть ссылки на все метки - collection.remove(check[id].placemark); и еще delete check[id];, чтобы не хранить её в памяти, GC её почистит при удобной ситуации.

ну и check логично переименовать в objects. или items, buildings - как душа пожелает.

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

Я примерно так и хотел сделать, только вот знаний по JS мало

Сейчас буду пробовать

Упростил

 

if (!check[this.id_place]) {

var myPlacemark = new ymaps.Placemark(...);

check[this.id_place] = myPlacemark;

myCollection.add(myPlacemark);
}

 

Алексей Yarrr!
28 января 2016, 04:05

Ну да, вариант чуть проще) Главное, что работает. Потихоньку и много опыта появится ;-)

Обращайтесь)

Алексей Yarrr!
28 января 2016, 04:05

В строчке check[object.id_place] = object.id_place должно было быть:

check[object.id_place] = object;