Клуб API Карт

Ускорение карты с объектами

Пост в архиве.
yourmary
27 января 2011, 16:11

Здравствуйте. Нужен совет гуру.

Есть каталог объектов, их более тысячи.  Задача была показать их на Я.картах.

Было сделано так: на сервере лежит ymapsml, который обновляется при обновлении каталога. Чтобы не дергать каждый раз базу.

Объекты имеют приоритет и minZoom, отображаются через ObjectManager. Чтобы не вешать браузер отрисовкой  большого количества dom-элементов.

Это казалось удачным решением, но - очень долго грузится этот самый ymapsml, больше метра он! Что задерживает отрисовку хоть чего-то на карте. Можно посмотреть.

Я пока вижу только один способ улучшить ситуацию - разбить xml-ник на части (Питер / Москва / все остальное), и грузить сначала один, остальные в фоне. Пока не знаю как, но, наверное, можно.

Какое хорошее решение этого вопроса?

14 комментариев
активные области - это:
- громадное количество файликов с тайлами (конечно, можно их объединить в блобы, или посадить на nginx, но это все равно - random access - делайте рэйды ;-)
- отсутствие возможности фильтрации, либо делать весь набор тайлов для каждого фильтра
- отсутствие возможности показа маркеров с тенью (либо надо опять же удвоить количество тайлов - один слой маркеры , другой их тени

Самое печальное - это, конечно, отсутствие фильтрации и перегенерация тайлов после добавления/удаления маркеров.

Есть ли выход?
Я нашел такой -  генерация тайлов "на лету".
Только не забрасывайте меня сразу тухлыми помидорами, сейчас расскажу.

Генерация тайлов на сервере "на лету" - довольно ресурсоемкий процесс. Гуру в этой области знаний, Kashey (он в этом клубе), проводил тесты. У него получалось 70-120 тайлов в секунду на кор кваде, причем, тестовый сервер работал только на генерацию тайлов, без выборок, расчетов координат и т.д. Тяжеловато...
Значит, такой путь нам не нужен. 
Но, поразмыслив пару месяцев, ко мне пришла интересная идея - а что если тайлы рисовать на клиенте? Все нормальные браузеры (не ie до 9 версии в смысле), поддерживают замечательный тег canvas, а их  - более 80%.
В результате был написан код, который дифференцированно подходит к рисованию тайлов, а именно:
- для имеющих canvas - я отдаю xml с описанием точек и и маркеров (там маркер берется из спрайта)
- для ie 7-8 - тоже трючок! они поддерживают data url, то есть в одном запросе/ответе я отдаю сразу все необходимые картинки 
- для остальных пещерных - каждый тайл рисуется сам по себе. это затратно, но на их долю приходится менее 5% запросов.

Запросы кэшируется браузером, поэтому нагрузка также снижается.
Так я решил проблему с рисованием тайлов. Сейчас на каждом тайле я размещаю до 100 маркеров, то есть на среднее окно с картой могу вывести много, очень много маркеров. И это - без нагрузки на dom!!! не то, что в realty.yandex.ru

Теперь, что касается фильтрации.
Эту проблема сейчас в стадии решения. Есть несколько путей, основная идея в которых - кэширование результатов фильтрации. То есть после фильтрации мы закидываем ВСЕ точки в кэш. А потом, оттуда забираем части для формирования тайлов. 
Сейчас это работает под mysql engine memory. Довольно затратный способ. Но уже дописывается собственный движок, главное в котором - сериализация результатов фильтрации. Сейчас на 10000 точек получается 500кб кэша, который закидывается в memcache и хранится в течение 5 минут. Скорость выборки по тайлам из кэша  - 10-15 мс, после этого - рисование.

Кстати, нумерация тайлов также оптимизирована. Каждая точка имеет базовый номер тайла. Это 32 битное число номера тайла на 16 зуме. Почему на 16? потому что, на 17 это будет уже 34 битное и не влезет в integer. А на 16 зуме у нас тайл размером 1х1 км (если не изменяет память), а значит, точек там - до 15. и если на 18 зуме мы отдадим все эти точки, при рисовании они просто вылезут за край канваса, да фиг с ними))

Базовый номер тайла расчитывается по quadtree в 4-значной системе (спасибо, Kashey), поэтому поиск точек для тайла на любом зуме сводится к оператору between.

Вот вроде бы и все...
Слегка сумбурно, будут вопросы - пишите, отвечу.
Проект на стади тестирования, поэтому не ругайте сильно : russia5.auto.ru
А может вы опубликуете отдельный пост про подобную генерацию? Детали реализации также интерсны.
к сожалению (или к счастью) я не использую в проекте api yandex'a, а пользуюсь гуглом. Решающих аргументов было несколько, среди них - отсутствие у яндекса поддержки touch-устройств, необходимость ключа для использования ip, и, еще что-то в api (сейчас не вспомню). 
A! вспомнил! перед запуском моего проекта, когда я уже был готов заниматься кластеризацией и т.д, гугл внедрил как раз похожую систему. Посмотрите, если в строке поиска у них ввести например, "азс", то получите россыпь "прыщей" на карте. причем, если посмотреть, то "прыщи" - это отдельный слой. А если хорошенько приглядеться, то в урле есть параметр цифровой, который, как я полагаю, содержит номер сервера, либо еще что-то похожее на то, где надо искать закэшированный фильтр. Вы можете вводить любые поисковые запросы, и, если вывод будет содержать достаточно много точек, получите мои любимые "прыщи".


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

Система встраивается в страницу 4-5(!) строками - это инициализация оверлея + callback на событие клика на маркере.
Серверная часть также очень универсальна.Результаты фильтра (после mysql) сливаются в класс кэшировани.
атем, система берет управления на себя)) 
там данные упаковываются, сериализуются и кидаются в memcache. При запросах на тайл в урле находится сама строка запроса (например,  "дилеры BMW"), по хэшу которой ищется кэш в мемкэше. если есть - очень быстро отдается результат, если нет (то есть запрос новый или "просроченный" (удаленный из memcache) - фильтр опять собирается , забирается из mysql (для этого есть callback функция) и пихается в кэш.
кстати, если вы заметили, мы "поделили" зумы на "нижный", "городской" и "областной".
в общем, интересного много :-)
еще добавлю.
реализована возвожность показа различных маркеров для точек. для этого подгружается спрайт и описание маркеров (размер, положение в спрайте,  якорь, тень + маркер при наведении с тенью), а в описании самой точки - название маркера. То есть количество различных маркеров и их размеров ограничено нашей фантазией и размером передаваемого спрайта.
еще одно решение - при клике выводится не та точка, на которую кликнули, а выбирается все точки из небольшой области вокруг маркера. Поэтому, если мы показываем не все прыщи, а только 100 на тайл, то при клике мы показываем в баллуне ВСЕ точки под кликом (на самом деле не больше 4 в баллуне, но наверху пишем количество под кликом). И это также делается без запроса к БД (с фильтрацией и т.д.), а вытаскивается из кэша.
Кста, на этой же технологии показывается правая панель с адресами.
Я прям таки покраснел :)
Вот только кластер у гугла малек не в задачу
да.... извини, неправильно написал твой ник, а исправлять тут нельзя свои посты (рейтинга не хватает, наверное))).
Про кластер - я сначала хотел делать по твоему методу (даже сделал), с кластеризацией, а потом увидел гугл, и сделал по-своему.)))


так у тебя просто и красиво получилось - прямо душа радуется.
Только вот сильно не хватает хинтов при наводе на точки :(
С некой верстноятью их прийдется ручками показывать в showPoint так как виртуальный маркер создается после движения зверем

спасибо!! ))



>> Только вот сильно не хватает хинтов при наводе на точки :(


с этим как раз и есть главная проблема.


если кэшировать с хинтом в 100 символов - на 10000 точек получается 1500 мб


так что думаем пока...


на самом деле система сейчас уже оптимизируется на 20000 точек в фильтре, чтобы установить ее на all.auto.ru - то есть на поиск автомобиля. например, объявлений по ЛАДА - около 20000...


 

конечно, не 1500 мБ, а 1,5 Мб
а ты глянь как сделаны те же активные области.
Суть похожа - тебе грузиться картинка, а вот описание области будет подгружено только когда мышка попадет в нужные пределы.
У тебя можно сделать как-то похоже - как только мышка долго будет в неком тайле, или при наведении на "прыщь"(за этот термин +100500) - подгрузи extended информацию.

Тем более что такое 1.5 мегабайт.
Для начала это 100кб или даже меньше в gzip при передаче данных клиенту, а во вторых -
>У тебя можно сделать как-то похоже - как только мышка долго будет в неком тайле, или при наведении на "прыщь"(за этот термин +100500) - подгрузи extended информацию.

не... этож тьма запросов к серваку :-@. 
>Тем более что такое 1.5 мегабайт.
Для начала это 100кб или даже меньше в gzip при передаче данных клиенту, а во вторых -
я, наверное, неправильно объяснил.
1.5 мега - это кэш ВСЕХ найденных точек. то есть совсем всех, на всем шарике. и он хранится на серве, чтобы не перегружать mysql однотипными запросами типо 
SELECT points WHERE model = 3 AND mark = 5 AND cat IN (12,23,2,2,54,23,54,12) AND tile_id BETWEEN 234355 AND 3434234
То, что подчеркнуто - и есть фильтр. Я вынимаю все точки до последнего условия (tile_id) и их пихаю в кэш. 
300 точек, которые выводятся уже на клиента -  канвaсом - это 20 килобайт, dataurl'ом- (нарисованные 24 тайла в одном файле - до 150 кб).
тогда я точно не понимаю где сложность передавать и хинты :)
И, кстати, какая у тебя необходимость в гугл-маркерах.
Вроде как хинты они тебе не дают - прийдется делать ручками, а infowindow можно открыть и так( индексация тебе вроде тоже не нужна )
Сложность не в передаче - в кэшировании на сервере. объем сильно растет.
Про гугл-маркер - по идее - это костыль. я сразу не нашел как сделать курсор "пальцем", вот и забил )))... 
в общем, в версии 1.0.1 это должно быть сделано.