Legends for Tiled map services don't display in Print output?

3944
8
Jump to solution
06-18-2013 12:27 PM
JoanSteinbacher
New Contributor III
I have downloaded the Basic Viewer template. I am creating my webmap with code (partial listing below).

        webmap.itemData = {           "operationalLayers": [              //Developable Land (display tiled service)            { "url": "http://gis.tpcmaps.org/ArcGIS/rest/services/LandUse/Developable_Land/MapServer",              "visibility": false,              "opacity": 1,              //"visibleLayers": [],              //"minScale": 100000,              //"maxScale": 0,              "title": "Developable Land",              "id": "devland",              "description": null            },            //Community-Based Planning Area (display dynamic map service)                                 { "url": "http://gis.tpcmaps.org/ArcGIS/rest/services/Websites/tpcCPA/MapServer",              "visibility": false,              "opacity": 0.6,              //"visibleLayers": [],              //"minScale": 100000,              //"maxScale": 0,              "title": "Community-Based Planning Areas",              "id": "cpa",              "description": null            }, 


I have added several operational layers (dynamic and tiled map services hosted on my server). All are being displayed in the map and legend as expected.

The problem is that when I print, the legend for the tiled map services are not being displayed on the print output.  I have tested with a single tiled layer with only 1 legend entry, so I know it's not a matter of the legend getting cut off.  The tiled service is displayed on the map of the printed output as expected.

Here's the addPrint function from the Basic Viewer. I commented out the 'legendLayers' setting so the legend will display.

function addPrint() {     var layoutOptions = {         'authorText': configOptions.owner,         'titleText': configOptions.title,         'scalebarUnit': (i18n.viewer.main.scaleBarUnits === 'english') ? 'Miles' : 'Kilometers',         //'legendLayers': [] //comment out so the legend will display by default, 6/13/13 jms.     };      //TODO - replace default templates with info from service     var templates = dojo.map(configOptions.printlayouts, function (layout) {         layout.layoutOptions = layoutOptions;         return layout;     });     // print dijit     var printer = new esri.dijit.Print({         map: map,         templates: templates,         url: configOptions.helperServices.printTask.url     }, dojo.create('span'));      dojo.query('.esriPrint').addClass('esriPrint');      dojo.byId('webmap-toolbar-center').appendChild(printer.printDomNode);      printer.startup();   }


Any idea why legends for tiled map services do not print?  I am using the ESRI print service, though I wouldn't think that would make a difference.

Thanks,
Joan
1 Solution

Accepted Solutions
ShreyasVakil
Occasional Contributor II
I would love to -- but how do I do that?  Sorry, the obvious things evade me sometimes...


Please see the attached image.
[ATTACH=CONFIG]25376[/ATTACH]

View solution in original post

0 Kudos
8 Replies
ShreyasVakil
Occasional Contributor II
You have to specify the LegendLayer for legends to show in the printout. Here is what I changed in the layout.js file for showing my basemap legend(tiled map service) in printout:
function addPrint() {

 var legendLayer = new esri.tasks.LegendLayer();
 legendLayer.layerId = "defaultBasemap";
 
    var layoutOptions = {
        'authorText': configOptions.owner,
            'titleText': configOptions.title,
            'scalebarUnit': (i18n.viewer.main.scaleBarUnits === 'english') ? 'Miles' : 'Kilometers',
            'legendLayers': [legendLayer]
    };

    //TODO - replace default templates with info from service
    var templates = dojo.map(configOptions.printlayouts, function (layout) {
        layout.layoutOptions = layoutOptions;
        return layout;
    });
 
    // print dijit
    var printer = new esri.dijit.Print({
        map: map,
        templates: templates,
        url: configOptions.helperServices.printTask.url
    }, dojo.create('span'));

    dojo.query('.esriPrint').addClass('esriPrint');

    dojo.byId('webmap-toolbar-center').appendChild(printer.printDomNode);

    printer.startup();
}
0 Kudos
JoanSteinbacher
New Contributor III
Thanks for leading me in the right direction.

To avoid hardcoding the layer ids for the legend, I've created a global variable 'legendLayers' that gets set in the buildLayerVisibleList function.

var legendLayers = [];  //use the same filtered list created in the 'buildLayerVisibleList' function in the legend dijit, added 5/23/13 jms
function buildLayerVisibleList(layers) {
    console.log("--> inside buildLayerVisibleList");
    var layerInfos = [];
    dojo.forEach(layers, function (mapLayer, index) {
     console.log("mapLayer.title: " + mapLayer.title + ", mapLayer.id: " + mapLayer.id);

     //If layer is for viewing popup info only, do not add to layer list. added 5/23/13 jms.
     //The id of the layer must start with 'pop' to filter it out.
     var isPopupLyr = false;
     if (mapLayer.id.substring(0,3) == "pop") { isPopupLyr = true} ;
     console.log("isPopupLyr: " + isPopupLyr);
     
     //Only include layers that are not used solely for 'popups', added 5/23/13 jms
     if (isPopupLyr == false) {
         if (mapLayer.featureCollection && !mapLayer.layerObject) {
             if (mapLayer.featureCollection.layers) {
                 //add the first layer in the layer collection... not all  - when we turn off the layers we'll 
                 //turn them all off 
                 if (mapLayer.featureCollection.layers) {
                     layerInfos.push({
                         "layer": mapLayer,
                             "visible": mapLayer.visibility,
                             "title": mapLayer.title
                     });
                 }
             }
         } else if (mapLayer.layerObject) {
             layerInfos.push({
                 layer: mapLayer.layerObject,
                 visible: mapLayer.layerObject.visible,
                 title: mapLayer.title
             });
         }
        }
    });
    legendLayers = layerInfos; //set global var for use with the legend dijit, added 5/23/13 jms
    return layerInfos;
}


I use the 'legendLayers' global variable in the addPrint function to create the list of layer ids for the legend.

function addPrint() {
 console.log("--> inside addPrint function"); 
 //legendLayers global var set in the buildLayerVisibleList fn. 
 //Array contains layer object, visible setting, and title.
 console.log("legendLayers.length: " + legendLayers.length); 
 
 //Create array containing the layer objects ids.
 var layerIds = [];
 //Loop thru each layer object and push id into the new array.
 for (var i = 0; i < legendLayers.length; i++) {
        //var layer = layers;
  console.log("legendLayers.layer.id: " + legendLayers.layer.id);
  var legendLyr  = new esri.tasks.LegendLayer();
  legendLyr.layerId = legendLayers.layer.id
  layerIds.push(legendLyr);
    }    
    console.log("layerIds.length: " + layerIds.length);
 
    var layoutOptions = {
        'authorText': configOptions.owner,
        'titleText': configOptions.title,
        'scalebarUnit': (i18n.viewer.main.scaleBarUnits === 'english') ? 'Miles' : 'Kilometers',
        //'legendLayers': [] //comment out so the legend will display by default, 6/13/13 jms. But this won't print the legend for tiled services...
        //'legendLayers': [legendLayer]
        'legendLayers': layerIds //set to the layer id array
    };

    //TODO - replace default templates with info from service
    var templates = dojo.map(configOptions.printlayouts, function (layout) {
        layout.layoutOptions = layoutOptions;
        return layout;
    });
    // print dijit
    var printer = new esri.dijit.Print({
        map: map,
        templates: templates,
        url: configOptions.helperServices.printTask.url
    }, dojo.create('span'));

    dojo.query('.esriPrint').addClass('esriPrint');

    dojo.byId('webmap-toolbar-center').appendChild(printer.printDomNode);

    printer.startup();  
}


In the function 'initUI', I had to move the call to 'addPrint' below the call to 'addLayerList' (which calls 'buildLayerVisibleList') so the global variable was set before addPrint needed it.

This approach works, though I have found the following legend print limitations which I'll need to address:

  • Space allotted for the legend in the out-of-the-box templates is too small

  • Layers in the legend don't display the layer's title.



Note: In case you're wondering, in the buildLayerVisibleList function, I'm filtering out any map layer that I've added solely for popup functionality, because I have added their corresponding map service so the labels display. Kind of a pain that individual layers won't display labels...
0 Kudos
ShreyasVakil
Occasional Contributor II
Thanks for leading me in the right direction.


The code I posted was just a proof of concept, just to demonstrate how to add the legends.

I am glad you found a good solution to dynamically create a list of layer ids!!!

It would be great if you could mark this as answer, might help other.

Thanks,
Shreyas
0 Kudos
JoanSteinbacher
New Contributor III
I would love to -- but how do I do that?  Sorry, the obvious things evade me sometimes...
0 Kudos
ShreyasVakil
Occasional Contributor II
I would love to -- but how do I do that?  Sorry, the obvious things evade me sometimes...


Please see the attached image.
[ATTACH=CONFIG]25376[/ATTACH]
0 Kudos
JeffJacobson
Occasional Contributor III

This is definitely a bug in Esri's code. According to the documentation:

If legendLayers is not specified, all operational layers will be present in the legend.

I recently encountered this problem and came up with this function to createLegend Layers.

/**

* Creates an array of LegendLayers of all layers currently visible in the map.

* @param {esri.Map} map

* @returns {esri.tasks.LegendLayer[]}

*/

function getLegendLayersFromMap(map) {

    var layer, legendLayer, output = [];

    for (var i = 0, l = map.layerIds.length; i < l; i += 1) {

        layer = map.getLayer(map.layerIds);

        if (layer.visible && layer.visibleAtMapScale) {

            legendLayer = new LegendLayer();

            legendLayer.layerId = layer.id;

            if (layer.visibleLayers) {

                legendLayer.subLayerIds = layer.visibleLayers;

            }

            output.push(legendLayer);

        }

    }

    // Return null if the output array has no elements.

    return output.length > 0 ? output : null;

}

// Sample call

var printParameters = new PrintParameters();

printParameters.map = map;

var template = new PrintTemplate();

template.format = "PDF";

// printUI is a custom class in my app that creates and manages the HTML UI for a print task form.

template.layout = printUI.getSelectedTempalteName();

template.layoutOptions = {

    authorText:printUI.form.querySelector("input[name=author]").value,

    titleText: printUI.form.querySelector("input[name=title]").value,

    legendLayers: getLegendLayersFromMap(map)

};

printParameters.template = template;

printTask.execute(printParameters);

JoelBennett
MVP Regular Contributor

This is definitely a bug in Esri's code.


Agreed (somewhat)...a look at _createOperationalLayers in PrintTask.js of v3.9 of the JavaScript API shows a switch for which there is no case for "esri.layers.ArcGISTiledMapService".  I think it's only a bug, though, if omitting this type of layer was unintentional.  However, I can't think of any good reason as to why they would omit it.  Whatever the case, their own documentation is, at minimum, misleading.

0 Kudos
AlexeiB
Occasional Contributor

I don't know if you had success with this, but were you able to have the legend appear in more than 1 column inside your print template? I have my map being printed to the custom template I made and the legend is also appearing but the problem is that it appears as 1 column and goes behind the map. I'd like to make it a 2 or 3 column legend so it spreads width-wise.

Thanks,

Alexei