Клуб API Карт

Печать карты (с метками, кластерами и блэкджеком)

nolan23
22 августа 2012, 18:06

Надо как-то статьей сделать )

Была поставлена задача - быстро сделать версию для печати карты с большим количеством меток и кластерами.

 

инициализируем карту как обычно - зум и центр берем из ссылки
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
        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 с картой тоже надо делать не больше этих размеров
            size = [650, 450],
формируем линку для статики
                '&z='+map.getZoom()+'&l='+stMapTypes[map.getType()]+
                '&size='+size[0]+','+size[1];
тут начинается магия
делаем див с абсолютным позиционированием и располагаем его посередине, так как привязка будет к центру вьюпорта. на самом деле, надо было вызывать 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-объектов)
один лишь баг - два копирайта.

 

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

9 комментариев
Подписаться на комментарии к посту

В рот мне ноги Дэвид Блейн, это какая-то уличная магия. o_O

Паш - ты мегакрутой Маньяк.

есть немного )))

И отлично у тебя меню и слои реализованны.

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

Не помнят войну.

 

спасибо за слои и меню.

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

кстати, вот думаю статейку написать про контролы. 

я их делаю абсолютно независимыми и вешаю на кастомные события карты.

типа, 

при изменении контрола кидаю событие 

map.events.file('objectsfiltered', {oldValue: oldValue; newValue: newValue);

в контроллере карты отрабатываю событие (в данном случае - фильтрацию) и кидаю событие 

map.events.file('datachanged');

в контроллере обрабатываю:

map.events.add('datachanged'. function() {

self.redraw()

}

это правильный подход? или, может, надо по-другому?

у меня событий получается с пару десятков)))

зато код прозрачный и все контролы - независимые

А что значит - независимые контролы? В том смысле, что вы из сами с нуля написали, вместо стандартных?

ненене.

вот например контрол типа карты.

он слушает событие typechange. и если оно наступает - перерисовывается.

одновременно с этим он сам может переключить тип карты.

и перерисуется от только когда услышит typechange.

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

то же самое и с остальными контролами. он перерисовываются при инициализации и по событию.

 

Ниче не поняла =) Видимо правда нужна статья.

Приветствую !!
Подскажите пожалуйста, а как можно вывести на печать построенный маршрут ?!