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

в 14:50, , рубрики: yandex map api, yandex maps, Яндекс API, метки: ,

Печать Яндекс.Карты под 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-объектов)
один лишь баг — два копирайта. при удалении с карты или даже скрытия сыпятся ошибки. для и ладно.

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

посмотреть можно тут

Автор: Pamarkin


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js