Пользователь
0,0
рейтинг
22 августа 2012 в 18:50

Разработка → Печать Яндекс.Карты под API 2.x с метками и кластерами

Всем известно, что напечать ядрекс-карту API 2.х с метками и кластерами просто так не получится. До сих пор карта строится не на канвасе, а на дивах с подложкой (background-image). Да и канвас любимому браузеру не поможет.
Была поставлена задача — быстро сделать версию для печати карты. Количество меток — более 600 + кластеры «из коробки».
Работающий вариант под катом

инициализируем карту как обычно — зум и центр берем из ссылки
ymaps.ready(function () {
        var mapTypes = ['yandex#map', 'yandex#satellite', 'yandex#hybrid', 'yandex#publicMap', 'yandex#publicMapHybrid'],
            map = new ymaps.Map($('#map')[0], {
            center:[ parseFloat(<?=  $request_lat?>),
                parseFloat(<?= $request_lng?>)],
            zoom:parseInt(<?= $request_zoom?>),
            type: mapTypes[<?= $request_mtype ? $request_mtype : 0?>]
        });

делаем принтер-френдли лейаут для метки
тут надо сказать, что принтер-френдли — это значит, что не используем background-image, а делаем кучу дивов с имиджами.
у меня иконка метки состоит из 2 частей — картинки и подложки. и то и другое — в спрайте. поэтому такой бешеный style. его я привожу inline для наглядности
        ymaps.layout.storage.add('voina#icon', ymaps.templateLayoutFactory.createClass(
            '<div style="position: absolute; width: 28px; height: 36px; overflow: hidden;z-index: 0; ">' +
                '<div style="position:absolute;width:20px;height:20px;overflow:hidden;top:4px;left:4px">' +
                    '<img src="/img/new_buttons_21.png" style="position:absolute;left:$[properties.iconOffset]px;"></div>' +
                '<img src="/img/buttons7.gif" style="position: absolute; left: -264px; top: -70px; "></div>'
            ));


то же самое — с иконкой кластера. заморачиваться с разными размерами иконок кластера под разное количество точек внутри я не стал — быстро надо было)
        ymaps.layout.storage.add('voina#cluster', ymaps.templateLayoutFactory.createClass(
            '<div style="position: absolute; margin: -26px 0 0 -26px; width: 58px; height: 58px; overflow: hidden;z-index: 0; ">' +
                '<div style="z-index:800;position: absolute; width: 58px; height: 58px; text-align: center; font-size: 13px; line-height: 58px;">$[properties.geoObjects.length]</div>' +
                '<img src="/img/cluster_big.png" style="position: absolute;"></div>'));


здесь получаем контейнер лейера самОй подложки карты
        var $container = map.panes.get('layers').getElement(),

так как типы карты у статики другие, делаем соответствия и получаем центр карты
            stMapTypes = {'yandex#map' : 'map', 'yandex#satellite' : 'sat', 'yandex#hybrid' : 'sat,ski', 'yandex#publicMap' : 'pmap'},
            center = map.getCenter(),


size — это засада. статик отдает максимум 650 на 450. тоже заморачиваться не стал — пускай будет максимум
div с картой тоже надо делать не больше этих размеров.
по-хорошему, если делать див с картой большого размера, нужен другой подход — через layer.
          size = [650, 450],

формируем линку для статики
            mapUrl = 'http://static-maps.yandex.ru/1.x/?ll='+center[1]+','+center[0]+
                '&z='+map.getZoom()+'&l='+stMapTypes[map.getType()]+
                '&size='+size[0]+','+size[1];

тут начинается магия
делаем див с абсолютным позиционированием и располагаем его посередине, так как привязка pane будет к центру вьюпорта. на самом деле, надо было вызывать map.panes.get('layers').getViewport() и считать центр. но при загрузке pane лежит посередине, а карту мы двигать не дадим
        $('<div></div>').css({
                position: 'absolute',
                left: -Math.round(size[0] / 2)+'px',
                top: -Math.round(size[1] / 2)+'px',
                zIndex: 800}).

вставляем внутрь img со статикой
            wrapInner($('<img>').attr({'src':mapUrl, width: size[0], height: size[1], border: '0'})).

и вставляем в контейнер лейера над остальными элементами
            prependTo($container);

хорошая функция — отключает все события карты. теперь ни сдвинуть, ни позумить
       map.events.removeAll();

тут я добавляю на карту свои маркеры. они у меня лежат в переменной window.data
        var len = window.data.length;
        if (len)
        {
            for (var i = 0, markers = [ ], properties, latLng; i < len; i++) {
                latLng = [parseFloat(window.data[i][1]), parseFloat(window.data[i][2])];
                markers.push( new ymaps.Placemark(latLng,
                    {   iconOffset: -window.data[i][5] * 20 - 1 },
                    {
                        iconLayout:'voina#icon',
                        iconOffset: [1, 2],
                        openBalloonOnClick: false
                    }));
            }

теперь кластерер. что такое margin — я не помню, но было на нормальной карте
       var clusterer = new ymaps.Clusterer({margin: [20]});

для иконок кластеров устанавливаем макет
          clusterer.options.set('clusterIconLayout', 'voina#cluster');

не хотелось писать эту функцию, но по-другому заставить кластеры не раскрываться не смог
          clusterer.createCluster = function (center, geoObjects) {
                var cluster = ymaps.Clusterer.prototype.createCluster.call(this, center, geoObjects);
// вот сюда я вписал все возможные отменялки поднятия события
                cluster.events.add('click', function(e) {
                    e.stopImmediatePropagation();
                    e.preventDefault();
                    return false;
                });
                return cluster;
            };

добавляем маркеры в кластерер
    clusterer.add(markers);

на всякий случай кластерер тоже заглушим
            clusterer.events.removeAll();

добавляем кластерер на карту
            map.geoObjects.add(clusterer);
        }
    });


тада!!! все работает и печатается (если браузер выдержит такое количества dom-объектов)
один лишь баг — два копирайта. при удалении с карты или даже скрытия сыпятся ошибки. для и ладно.



посмотреть можно тут
Павел @Pamarkin
карма
23,0
рейтинг 0,0

Похожие публикации

Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (13)

  • 0
    Странно у меня на демо открылась пустая область вместо карты.
    Браузер Google Chrome 21.0.1180.79 m.
    • +1
      действительно странно. наверное, все-таки DOM не выдержал и таки рухнул))
      • 0
        Не расстраивайся. Сделаешь еще. Хорошо, когда люди стараются исправить косяки разработчиков.))) А твоя идея очень оригинальная. Если получится оптимизировать идею, то обязательно напиши в ЛС. Я тоже пытался сделать, но у меня это кончалось развалом карты на куски.
        Кстати на лисе, опере и осле 10 полет нормальный, только хроме капризничает.
        • +1
          У меня в хроме работает под винду и макось. И еще под сафарями. И под ios ;-)
  • 0
    А чем Static API не угодил? Отдает картинку с метками, отлично печатается.
    • 0
      Статик не отдает кастомные плейсмарки, а главное, кластеры
  • +2
    А копирайты у меня неплохо скрылись с помощью CSS. Никаких ошибок.
    • 0
      Напомню, что это нарушение лицензионного соглашения.
      • +2
        Там 2 копирайта получается — один от api, другой от статика. Думаю, нарушением не будет один из них убрать
        • 0
          Если один останется, то нарушения не будет.
  • 0
    Спасибо за информацию, попробую на своём сайте что-то подобное сделать. Правда что-то слишком сложный способ получился, но конечно лучше чем совсем ничего.

    А каким образом реализована печать в самих Яндекс.картах? Там если печатать обычную страницу то также маркеры пустые получаются, а если нажать кнопку «Печать» то всё выводится на печать хорошо.

    Только мне не удалось выяснить каким образом у них так получилось сделать, но навряд ли такую же кучу jquery они используют для этого.
    • 0
      Протёр глаза и посмотрел повнимательней, всё же там используется что-то типа Static API в версии для печати т.к. всё выводится сведенной картинкой. А в Static API можно как-то указать не конкретные координаты метки, а сделать поиск как в обычных картах?
      Что-то типа
      var myGeocoder = ymaps.geocode(myAddress);
      • 0
        нет. можно сначала сделать геокодирование, а потом запрочить картинку с статике.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.