Как стать автором
Обновить

ObjectManager в API Яндекс.Карт. Как быстро отрисовать 10 000 меток на карте и не затормозить всё вокруг

Время на прочтение10 мин
Количество просмотров111K
Всего голосов 93: ↑89 и ↓4+85
Комментарии85

Комментарии 85

Спасибо! Как раз заканчиваю делать GPS трекер и этот момент для меня очень актуален.
Раньше маршрут отображался больно задумчиво.
У трекеров свои заморочки, я недавно делал отображение трека, довольно не тривиальная задача оказалась, но в итоге удалось с хорошей производительностью отрисовать трек из 60000 точек на WP. Очень сильно осложнялось, тем что пришлось все рисовать в 3D, так как в WP 8 нет поддержки 2D графики :)

Получилось вроде не плохо cdn.marketplaceimages.windowsphone.com/v8/images/46a9e67d-df20-4a8d-b641-1ce2db649c09?imageType=ws_screenshot_large&rotation=0
Симплификация и немного другие алгоритмы «раскраски» могли бы спасти.
Симплификация используется по полной, более того 60000 это количество маршрутных точек, прошедших симплификацию на сервере.
А что с алгоритмами раскраски?
На картинке не более 2000 точек, а по хорошему можно и в 200 нарисовать — измените параметры симплификатора :P
А «алгоритмы раскраски» в общем случае зависят от рендера.
Если 3D — то заливаем в точки еще значение «расстояние от начала кривой», и по этому значению через lookup в 1d текстуре(прийдется сгенерить) красим. Можно хитрее. Но в любом случае — один вызов рендера.
На картинке 600 точек :)
Это скриншот из магазина приложений, но ситуация когда надо будет рисовать все точки, вполне реальна.
А там и так не много вызовов рендера, обычно 6 дроколов на проход, больше только для треков с огромным количеством поворотов.
Раскраска не из текстуры, а просто математика в шейдерах, получается быстрее терстур и главное стабильно качество на различных резрешениях и размерах экранов.
На самом деле большую проблему для меня представляла тесселяция и отрисовка стрелок направления.
Ну и математика для пересчета гео координат на GPU тоже заняла ощутимое время.
И стрелки и гео координаты — решаемые проблемы. Так есть хитрые варианты «ступенчатых» оптимизаций, которые у нас в АПИ называются «renderFlow».
Надо будет как-нибудь о них рассказать.
Да они решаемы и решены. После своих изысканий, думал написать статью, но мне запретили :)

Еще один интересный «прикол», что бы использовать кеширование хотел делать расчеты на процессоре, а потом просто отрисовывать. Не тут то было, начали появляться мизерные но отклонения, которые были хорошо видны на маленьких масштабах.
Расчеты чего? И отклонения в какую сторону?
Быть может проблема идет из 16-32-48-64 битных переменных?
Расчеты положения стрелок направления, я передаю вектор направления и расстояние от начала участка. Если считать на GPU то все координаты совпадают. Если считать на процессоре, результат скачет вокруг нужной точки. Расчеты везде проводил в 32 разрядных вещественных, потому, что видеопроцессор другого не умеет, а вот процессор вероятно считает в 64 бита, а потом приводит к 32. В общем проблема скорее всего в округлении. Добраться до расчетов шейдеров я не смог, поэтому просто принял за данность.
«Впервые» Ремот был презентован на Яндекс.Субботнике в Минске — events.yandex.ru/lib/talks/2217/
Еще тогда в презе мелькала ссылка на серверный кластеризатор, но в те времена он работал не очень шустро.
Буквально пару дней назад его автор (dimik) обновил библиотеку и она теперь работает с теми самыми «пространственными ключами», конкретнее с Z-curve. И работает очень быстро.
Опен сорс, монго, map-reduce, агрегация — github.com/dimik/geohosting-server/blob/master/storage/model/feature.js#L66
Хорошая реализация, но всё лишь для меток. А будет ли что-то в этом роде для полигонов (особенно актуально решение из коробки для кластеризации полигонов, кастом при увеличении более 13 не хочется постоянно использовать)?
Для полигонов решение будет. Правда с кластеризацией неточечных объектов на клиенте картинка пока туманная.
Быть может сделать дополнительную опцию какую-либо, по верхней точке кластеризировать? В кастоме сейчас использую именно так, а при зуме > 13 уже отрисовываются сами полигоны.

P.S. Задача вывести более 1 500 полигонов на старте проекта.
А точно нужно вывести 1500 полигонов?
Если нужно — зачем прятать. Если склеивать то во что?
Стандартные кейсы (n.maps.yandex.ru,wikimapia.org) отображения полигонов на карте обычно реализуют два механизма:
1. «Не показ» невидимых полигонов(не считая что оба сайта работают на «активных областях», те тайлах). В АПИ карт для графики это выполняется автоматически.
2. «Не показ» «мелких» обьектов на текущем зуме — именно это, а не кластиризатор, дает «разрежение» данных. Оно же — различные наборы данных для различных зумом.

А насчет кластеризации по одной точке могу рассказать о случае esosedi:
Там, не смотря на то, что многие обьекты площадные система работает только с точками. В случае полигона это левый верхний угол.
Далее работают два момента — при запросе с сервера приходят только те полигоны, которые имеют угол в запрашиваемом тайле. С учетом маргина с сервера на клиент не передаются обьекты больше чем 512 пикселей на текущем масштабе.
Потом работают просто сортировки — если вы знаете что не можете показать больше чем 500 обьектов — отсортируйте те что есть, и покажите только 500 из них.
Например те, чьи линейные размеры меньше 512х512, далее order by size, и не более чем можно. Или не более чем нужно.
При этом обьекты, линейные размеры которых на текущем зуме меньше некого значения (размер дива карты/64) считаются точечными и кластеризуются как точки.
А если нужно показать все — ну судьба такова.

К чему я это — лучше вас никто ваши данные отобразить не сможет. Написаться свой ObjectManager не так чтобы сложно. Только это (почти) никто не делает.
Только что заметил, что на я-картах рисуются какие-то иероглифы при поиске.
Оказалось это цифры меток загрузились до самих меток и показываются друг над другом.
Потом закорючки тоже появились, но выглядело забавно.
Ровно так было сделано в первой версии API: tech.yandex.ru/maps/doc/jsapi/1.x/ref/reference/hotspots.loader-docpage/
Однако, что же в нём «более красивого» — большой вопрос. Такая схема подразумевает обязательное наличие в глобальной области видимости статического объекта (StaticJSONP), а это на один глобальный объект больше, чем хотелось бы. Статические же имена callback-ов на клиенте можно создавать и удалять динамически, как и в обычном jsonp.
ну вот выходит либо ноль глобальных имён (при расширении JSON), либо одно глобальное имя (при использовании StaticJSONP) против десятков глобальных имён, пусть и короткоживущих

впрочем, в этом году уже очень мало смысла обсуждать JSONP :)
… хотя в «этом году» только pastvu использует WebSockets для транспорта данных карты…
для самых модных — WebSockets, для более простых — CORS
И гнать все в формате csv как экспорт из базы данных.
Зачем весь этот синтаксический мусор JSON?
потому для него в браузере есть парсер на C
Как и другие строковые функции :P
При этом трафика создает сильно больше, даже после gzip
Имхо — для однотипных табличных данных, а это самый частый кейс для ObjectManager, «табличный» экспорт — лучшее решение.
JSON как красивый и «глубокий» формат полезен когда можно что-то сильно по разному расписывать. Но тут можно воспользоваться серверной подгрузкой, а не качать все эти данные на каждый запрос.
я бы посмотрел сравнение парсера на JS и встроенного парсера

а ещё на степень сжатия JSON
Года три назад тесты проводились. Не в пользу JSON. Тем более не в пользу генерации JSON.
Попробую поднять данные, а вообще один знакомый(Alexey Perchenok) почти на эту тему диплом защищал(или докторскую?) и вроде успешно.
ну как, получилось поднять данные?

а я сегодня ещё и про JSON Homogeneous Collections вспомнил, у них ни с размером (как у JSON), ни со скоростью парсинга (как у CSV) не должно быть проблем
Нормальных цифер, которые можно было бы на графике показать — нет. Пну кого надо сегодня.
Но JSONH чтука странная (вложенности то не поддерживает), но каких мутантов только не бывает.
CSV тоже не поддерживает, а именно с ним JSONH соревнуется
Не соревнуется, а просто ничем не отличается.
Более того — в случае равномерных данных можно было бы заголовки передать один раз, а не в каждой строке.
По сути JSONH просто создает строчку где в начале идут ключи, потом значения.
Я думаю, в 99% случаях, ключи и так известны с обеих сторон.
да, очень похож, но я бы не стал говорить, что совсем ничем не отличается
Расширять чужой JSON (мы же API, работаем в контексте другого сайта) мы не имеем права. По той же причине не можем заводить в глобальной области переменные с неспецифичными именами типа StaticJSONP — мало ли, вдруг у вебмастера есть своя переменная StaticJSONP.
ну можно и в своё пространство имён положить
хотя ещё раз скажу, сейчас тема уже потеряла актуальность
Тогда этой системой сможем пользоваться только мы, вебмастер не сможет.
Короче, проходили мы всё это.
Но вот что мешало в последнем этапе, по сути «транспорте» оставить дырку для user реализации? До сих пор понять не могу :(
Еще можно не изобретать велосипед и использовать стандартизированные технологии. протокол OGC WFS вполне позволяет тянуть данные (полигоны, линии, точки) с сервера (сервера — в смысле «готового серверного приложения»), передавая серверу только область покрытия (bounding box). При этом решать вопрос перегруженности области можно сразу и на стороне клиента (используя кластеризацию, которую позволяет делать тот фреймворк, который выводит собственно карту) и на стороне сервера (используя для хранения объектов БД с пространственными расширениями и адаптируя запрос к ней в соответствии с величиной bounding box.

Простор для оптимизаций под свои нужды — огромный, например, можно закэшировать центроиды полигональных объектов (а не считать их каждый раз), чтобы показывать точечные маркеры вместо полигонов для тех масштабов, где величина каждого конкретного полигона меньше величины точечного маркера. Интервалы этих масштабов — тоже закэшировать.
Все, что вы описали, прекрасно работает в связке с клиентскими модулями, описанными в статье
Это был, скорее, ответ на некий оттенок изложения в статье, из которого можно легко сделать ошибочный вывод, что ваша реализация — чуть ли не единственное спасение от тех проблем, которые вами описаны.
Ну я действительно не знаю про клиентские библиотеки, работающие в связке с популярными карточными апи, который управляют оптимальной загрузкой данных. Буду благодарна, если приведете ссылки на такие решения
Если под загрузкой вы понимаете загрузку с сервера, а не показ, то вопрос чаще всего решается именно на стороне сервера, опираясь на bbox и запрошенный набор слоев. А клиент занимается уже только показом, стилизацией, кластеризацией (для того же Leaflet есть прекрасные варианты ее реализации) и подгрузкой свойств (WFS getfeatureinfo) вслед за действиями пользователя.
Я имею в виду вот что. Вот у вас есть карта, у нее есть видимая область. Вы берете эту область и делаете запрос на сервер за данными. Данные пришли, их нарисовали — все ок.
Дальше пользователь меняет зум карты. Клиент должен понять, какие данные у него уже для новой видимой области карты загружены, а какие нет. И сходить строго за недостающими данными. Ну и аналогично при сдвиге карты.
OpenLayers поддерживает Tiled-режим классического WFS уже много лет. Чуть менее классический WMTS (который может содержать как растровые, так и векторные данные) тоже дофига где работает, включая OL и Leaflet. ESRI также имеет для своих feature layer-ов в ArcGIS Web API такую же штуку. И ниже я уже писал вашему коллеге, что разбиение на тайлы — это всего лишь один из видов оптимизации, который еще и не всегда полезен.
Э-э-э… А никак нельзя отправить запрос типа «сессия та же, зум стал меньше вдвое, дай-ка мне, сервер, дифф»?

Зачем клиенту что-то понимать?
Насколько я понял — именно так работает pastvu (WebSockets), но это на самом деле сложно сделать. В том числе из-за большого latency.
Обычно получается как-то так
events.yandex.ru/lib/talks/2217/ — в самом начале презентации разбираются варианты загрузки и обьясняется несколько стандартных косяков.
Если лень смотреть немного расскажу:
1. Кроме зума есть текущее «окно» выборки — активная область на карте.
2. Окно может быть любым
3. Одно и тоже окно в гео координатах может указывать на различные окна в пиксельных, но на разных зумах.
4. И вы не можете передать все данные с сервера на клиент.

Результат — прыгает и скачет, неравномерное покрытие карты и тд и тп. И чем «умнее» сервис — тем хуже(по этим параметрам) в итоге работает. Как не странно.

PS: Только тайлы, только стабильная выборка. А если хотите одним запросом — ничего не мешает послать один большой запрос с указанием загружаемых квадрантом и получить один ответ с кучей данных. Флаг splitRequest.
Стоп-стоп-стоп.

Пока я не смотрел видео, задам вопрос из незамутненного избыточным знанием мозга. Прозой: «что такого клиент не может передать на сервер, что создает ситуацию „клиент знает, а сервер нет“?».

Пьесой:
————
Начало сессии.

Клиент (серверу): дай данных для lat/long/zoom/whatever.
Сервер: ннна!
Клиент (рисует).
Юзер: тап-тап-тап.
Клиент: у меня тут юзер обнаглел, хочет теперь lat/long/zoom/whatever2 и, пока мы тут кофе пили, он еще успел экран сменить на ретину.
Сервер (хранилищу): дай всё для lat/long/zoom/whatever2.
Сервер (делает дифф).
Сервер (клиенту): ннна!

Не помню когда я про это рассказывал, но вы явно бородатый и родом из gamedev.
Ибо описали типичную ошибку. Или не ошибку, а «привычку» что клиент и сервер это что-то цельное, что-то рядом.
Хотя нет. Любой «приставочник», у которого носитель — CD, а не SSD имеет ровно туже проблему — можно иметь хорошую скорость канала, но латенси…
А в вэбе между вами километры проводов и этот ужасный http. И, конечно же, клиентов у вас СИЛЬНО больше чем серверов.
Без организации относительно полноценных мозгов как на сервере, так и на клиенте — ваши сервера взорвуться!
Му-ха-ха-ха-ха!
Ну или можно покурить теории и практики сетевых предсказаний еще кваки первой. С тех пор суть проблемы мало изменилась.
www.gamedev.ru/code/articles/?id=4258
en.wikipedia.org/wiki/Client-side_prediction
Я к геймеву никогда даже близко не подходил, но дело не в этом.

Мне очень хорошо понятно, что бесплатный ботнет это круто, и этим необходимо пользоваться. Но в данном конкретном случае: а) провода — вообще не в кассу, если подумаете — сразу поймете, почему (вне зависимости от того, кто придумал структуру диффа, передать дифф придется); б) эффективнее эту задачу решать на сервере, даже когда он один у разраба под столом (у сервера есть куча дополнительных способов снизить нагрузку, да и при правильной архитектуре построить дифф не должно быть ощутимо затратнее, чем просто сплюнуть данные); в) prediction — это хорошо и нужно, только вы все путаете: prediction в данном случае относится к «уловили движение глаз юзера влево и запросили этот кусок не дожидаясь скролла».

И, наконец, г) я понимаю и принимаю аргумент «мы хотим разгрузить наши сервера», правда я пока не понимаю, удалось ли решить эту задачу в данном конкретном случае. Пока складывается впечатление, что нет.
Все плохо :(
Представим что я вот
1. открыл вашу карту.
2. передал viewport
3. сервер сделал выборку из бд
4. передал мне 100 маркеров и запомнил какие
— прошло, кстати, 50-300 мсек.
5. я передал новый viewport (он может отличаться на 10 пикселей)
6. сервер сделал выборку из бд (о, 2д индекс это круто)
7. сервер начал искать диф между 100 старыми и 100 новыми. В худшем случае у меня теперь 200 точек
— будем считать, что 100-500 мсек

100. сервер сделал выборку из бд
101. диф между 100500 тем что уже отправил, и новыми 100.

На самом деле умрет на выборках из бд — потому что их нельзя закешировать. В случае загрузки по дискретным границам — можно.
Конечно никто не запрешает держать «дискретные» границы на сервере, но дифать сложно будет.
Плюс дифы — просто зло. Выборка должна быть _стабильна_ вне зависимости от внешний условий.

PS: Мы, конечно же говорим о двух случаях:
1. У вас много пользователей и много данных
2. У вас мало пользователей, средне данных, но и сервера на виртуалке…
> 6. сервер сделал выборку из бд (о, 2д индекс это круто)
> 7. сервер начал искать диф
А выборка не может быть сразу диффом? Я же не знаю архитектуру, может быть, и не может, но вроде бы никто не в силах помешать поднять рядом инстанс, из которого можно сразу забирать дифф. Ему же даже репликация мгновенная не нужна, маркеры это не пробки, ежесекундно не должны меняться.

> 101. диф между 100500 тем что уже отправил, и новыми 100.
Зачем это? Обычно диффы делают супротив предыдущей версии, а не момента перед «И создал бог свет».

> Плюс дифы — просто зло.
Мы точно в одной и той же ветке? :)
Мой первый комментарий («а почему не сервер?») был реакцией на «дифф должен определить клиент».

Давайте я еще раз скажу: я понимаю, почему в вакууме лучше все, что можно, — считать на клиенте. Я даже теорию для чайников прочитал.
Но в данном конкретном случае я не вижу выгоды. Все еще не вижу (возможно, это мои проблемы, не спорю).

Я понимаю, что серверу не помешает сразу отдавать все восемь квадратов вокруг, а клиенту предупредительно ходить за «еще» по факту скролла/зума. Это как раз то, что вы назвали «prediction». Но зачем клиенту вычислять, какие ему данные нужны — убейте, не представляю. Не говоря о том, что клиент вообще-то не обладает полнотой информации на данном этапе (например, он может не знать, что плотность покрытия на соседнем участке гораздо выше и с этим надо что-то сделать, или: соседний участок пуст, поэтому вот тебе следующий, чтобы пустые пакеты не гонять).
В том и проблема, что никто не видит проблемы.
Тормознутый js, ширина канала, latency и RPS — всего три параметра, которые усложняют жизнь.
И нельзя просто взять и загружать данные по bbox — нужна сетка.
Разбиение на тайлы — всего лишь одна из форм оптимизации. В общем случае оно не является обязательным.
Даже, скорее, так: польза от разбиения данных на тайлы зависит от характера распределения данных в пространстве, относительной нагрузки на сервер и наличия функций выборки объектов. Когда клиент имеет возможность выбрать, условно, любые 49 слоев из 135 возможных (нормальная ситуация для ГИС, но редкая — для «плоских» сервисов), а распределение объектов не является сколько-нибудь равномерным, разбиение на тайлы становится откровенно вредным, увеличивая число запросов ради «полупустых» тайлов.
Для таких случаев существует понятие «пирамиды тайлов», про которое немного знает LoadingObjectManager.
А вообще про «загрузка по квадродереву», когда для адресации «тайла» используются Z-коды лично я рассказывал года 3-4 назад (на CodeFest). А по факту — Wikimapia использует такой механизм уже лет 6, если не больше.

Как бы не было идеи придумать что-то новое — ничего прорывного в представленной «технологии» нет. Была идея предоставить полезный инструмент тем людям, которые за прошедшие годы до этого сами не дошли.
Я тут возражал немного по другому поводу (по поводу того, что сетка не всегда нужна и не всегда вообще полезна), возможно вы интерпретировали «слои» как «масштабные слои», но я этого не имел в виду.
У Викимапии или условного «сервиса с банкоматами» — один слой данных, возможно — разные стили внутри него.

А в общем случае (ГИС, а не картографический сервис) тематических слоев может быть сто. Резать каждый на тайлы и отдавать пользователю столько, сколько он хочет, из этой сотни на каждую ячейку сетки — расточительство, особенно если неизвестно, есть ли сколько-нибудь значимое число данных в каждом тематическом слое. В этом случае отдавать данные целиком на bbox вполне может оказаться экономнее уже из-за снижения накладных расходов на передачу.
Покурите gdeetotdom.ru/map на момент загрузки слоев данных (их там ~30). Так, например на сервер идет запрос вида:
www.gdeetotdom.ru/map/tiles/14-42-39-48-38l032/103/002/2/
14-42-39-48-38 — это номер «слоев» которые надо отдать из тайла 032/103…
Никакого оверхеда, и со 100 слоями тоже будет работать хорошо.

У викимапии, кстати, слоев — очень много. Можно увидеть если сделать поиск и/или выбрать какую либо категорию.
Разбирать XML на клиенте? Спасибо, но нет.
Не говоря уже о том, что там гигантский оверхед по функциональности (10+ разных методов), но все равно не покрыты базовые кейсы типа задания стилей меткам.
Недавно пригодилось, хорошая вещь (использовал LoadingObjectManager), но местами волосы начинали шевелиться в разных местах тела. Например от таких вещей:

Нужно было динамически загружать данные в balloon метки, т.е. для метки из менеджера (из коллекции loadinObjectManager.object) по которой был клик, нужно было установить свойства, тут начинается интересное: вот тут описан метод setObjectOptions, но не описан метод setObjectProperties, а на самом деле он есть, нашёл через консоль хрома.

На документации приключения не закончились: кликаем на метку, открывается пустой бален, после получения ответа на ajax-запрос делаем

objectManager.objects.setObjectProperties(obj.id, {
	balloonContent: data
});

и балун остаётся пустым, но стоит сдвинуть карту хоть на пиксель — контент появляется.

Описанные ситуации имели место в версии 2.1.16, на 2.1.17 не проверял.

И кстати, здесь пример jsonp ответа с синтаксической ошибкой. Это не помешало ни сколько в разработке, но удивило.

Не судите строго за, может быть, негативный оттенок комментария, просто накипело.
Метод не включили в документацию, так как не хотим, чтобы его использовали. Для загрузки содержимого балуна по требованию мы рекомендуем другой способ и описали его в примере в песочнице.
Можете уточнить, в чем ошибка? Мы в ближайшее время поправим.
Просто метод вполне себе публичный, в противном случае я бы его бы даже и не заметил.
Способ из песочницы мне почему-то не подошёл(уже не вспомню по какой причине), что меня и побудило к раскопкам.

Ну раз мой шаловливые руки докопались до нерекоммендованного метода, значит это не совсем баг :).

Ошибка проявлялась примерно так
objectManager.events.add('balloonopen', function(e) {
	var d = e.get('target').getData();

//	открывается балун с прелоудером
	loadingObjectManager.objects.setObjectProperties(d.id, {
		balloonContent: 'Загружаю…'
	});


//	getBalloonData посылает запрос для получения данных и возвращает Promise
	getBalloonData(d.id).then(function (data) {
		objectManager.objects.setObjectProperties(d.id, {
			balloonContent: data
		});

//		ответ пришёл, метод setObjectProperties отработал, но по прежнему видим балун с надписью «Загрузаю…», если сместить немного карту мышкой или даже методом map.panTo новый контент балуна появляется мгновенно

	});
});


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

А есть ли какие-нибудь примерные сроки релиза поддержки objectManager`ами полигонов?
Когда objectManager`ы будут поддерживать все типы геообъектов, цены им не будет!
Вот корректный способ решить вашу задачу jsfiddle.net/hn2fnxku/.
Я посмотрела — у нас не подтянулся в документации соответствующий пример к методу, поправим.

Что за синтаксическая ошибка в примере с json?

Поддержка полигонов уже в процессе разработки, но по срокам сейчас не могу ничего сказать.
Вот здесь после таблицы с параметрами 2 примера, во второй
jsonp_callback({
    // Ответ содержит поля error и data. В случае ошибки в поле error
    // пишется код ошибки или ее описание.
    error: null,
    data: {
        {
            type: 'FeatureCollection',
            features: [
                 {
                     type: 'Feature',
                     geometry: {
                         type: 'Point',
                         coordinates: [55, 35]
                     },
                     id: 23,
                     properties: {
                         balloonContent: 'Содержимое балуна метки',
                         iconContent: 'Содержимое метки'
                     },
                     options: {
                         preset: 'islands#yellowIcon'
                     }
                 }
            ]
        }
    }
});


data лишний раз обёрнута в {}
Спасибо, поправим
Но воз и ныне там
Написал не для того, чтобы лишний раз тыкнуть "ах вы такие", а для чтобы как-то ускорить этот процесс, я лично отправлял много фидбеков про ошибки в документации к АПИ, но они не правятся — «Спасибо, ваше обращение очень важно». Все.
В документации на релиз-кандидат ошибку уже поправили. Что вы так горячитесь? Минорная же правка)
Ну тогда и здесь исправьте код ответа )
ок)
Была месяцев 8 назад задача отобразить 32к точек, в итоге единственным адекватным решением в плане сложности реализации/скорости оказалось:
1) Предварительная кластеризация для малых масштабов
2) Для крупных масштабов предварительно получать прямоугольник отображаемой области, вручную отрезать точки, которые не попадают в прямоугольник, и отображать только их, используя кластеризатор.
Я тоже как-то делал подобное. Нашел библиотеку от хорошего человека.
Скорость можно оценить на этом сайте, там сейчас как раз 32к точек отображается:
facebiz.info/search/
Выберите слева плитку «Торги высвобождаемого имущества»
и в открывшемся окне нажмите «Найти»
<Зануда мод он>У вас на карте при поиске не видно копирайтов, что нарушает пользовательское соглашение</Зануда мод офф>. А по делу да — шустро работает.
А существует какие-нибудь открытые CMS на эту тему (можно в виде плагина к Wordpress)? С одной стороны — форма добавления точек в базу, а с другой — карта со всеми этими крутыми оптимизациями.

Иногда хочется сделать что-то вроде локального сервиса помощи или карты интересных событий в ближайшей округе, но количество тонкостей, которые надо реализовать для приличного результата — пугает.
А подскажите, для яндекс.пробок слоя есть возможность получить информацию не графическим слоем единым, а набором данных пригодным для анализа? Не просто отразить графически загрузку по маршруту для визуальной оценки, а для чтобы выполнить автоматическую оценку загруженность? Не совсем по теме статьи, но просто по случаю, раз специалисты по яндекс.картам на хабре.
Публичного api для решения вашей задачи сейчас нет
В общем, как говорится: «хорошая мысля — приходит опосля».
Скрытый текст
Несколько лет назад, потестировали несколько кластеризаторов (в том числе и от яндекса), и в итоге остановились на своём (конечно, он не так хорош, как ObjectManager). Но в нём не происходит инициализация меток для карты, пока метка не оказывается рядом с границей видимой части карты, группировка меток привязана не к каждому значению зума. Работа по группировке проводится не с объектами-метками, а с исходными данными. Сейчас эти хорошие мысли описаны в этой статье. На тот момент показал неплохое быстродействие среди прочих кластеризаторов, а общие принципы позволили достаточно легко подключать этот кластеризатор к другим сервисам карт. В итоге и сейчас продолжает работать. Теперь будем тестировать новый ObjectManager
Хочу задать вопрос здесь, может кто-то из Яндекса ответит т.к. в клубе разработчиков карт ответы есть только на базовые вопросы.

Суть в том, что мне нужно чтобы LoadingObjectManager не кешировал результаты, а при каждом действии обращался к серверу за данными, это делает RemoteObjectManager, но он не кластеризирует сам, а кластеризация нужна.

Как отключить кеш в LoadingObjectManager?
Такие вопросы лучше задавать в клубе, потому что тогда другие пользователи смогу найти ответы на их вопросы, не создавая новых тем. А отвечают и тут и там все равно одни и те же люди.
Сейчас такой возможности нет, но мы подумаем о добавлении такой функциональности и будущих релизах.
Скажу так: это очень важный функционал. RemoteObjectManager — это уже хардкор уровень, который не часто может понадобится, а LoadingObjectManager является как раз нужной в большинстве случаев серединой.

Ещё вопрос: как заставить LoadingObjectManager перезагрузить текущее состояние меток програмно? Т.е. взять и без изменения масштаба заставить его очистить кеш и получить данные снова? (немного другой вопрос чем был ранее)

Сейчас это возможно сделать только удалив-добавив менеджер с карты. В будущем добавим метод для этого кейса
Надеюсь таки будет программный reload для LoadingObjectManager
Да, выйдет в паблик через релиз
Ещё один баг про LoadingObjectManager:

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

(Это нужно и в топик и в документацию добавить) что LoadingObjectManager при отключенном splitRequests (по-умолчанию) при изменении масштаба посылает на сервер несколько запросов за данными по прямоугольным областям вокруг ранее видимой, а не один запрос как описывалось (или в топике или в документации). При включенной splitRequests запросы будут на каждый тайл, при выключенном — splitRequests на целые блоки тайлов вокруг области, которая уже видна.

Это важно чтобы понять описание бага: т.е. на изменение масштаба в 1 пункт на сервер отправится 4-5 запросов на области вокруг, данные ведь кешируются и запрос на видимую область карты не посылается. Это отличие от RemoteObjectManager, который пошлет реально один запрос на всю новую область карты (это то, что я и просил выше: добавить в LoadingObjectManager отключения кеша и посылку одного запроса на всю область)

Баг: что происходит

Если резко уменьшить масштаб колесом мышки на 2 порядка, то на сервер будут отправлены 2 группы запросов (по 4-5 внутри) для каждой области вокруг ранее показанной. И тут самое интересное: ответы на эти запросы могут приходить назад не в том порядке в каком они были посланы к серверу.

И если ответы второй группы запросов пришли раньше первой (что вполне может быть — загружена бд и просто нагрузка на сайт была), то метки той первой группы, ответ на которую придет после второй не будут показаны.

Это очень важный баг, я о нем писал, постарайтесь командой его рассмотреть внимательнее.

Я даже макет сделал, уж очень хочется закрыть этот баг:
Большое спасибо за подробное описание, обязательно починим
Зарегистрируйтесь на Хабре, чтобы оставить комментарий