Tutorial: Basic web map printing/exporting using arcpy.mapping

Complexity: Intermediate Data Requirement: ArcGIS.com

In this tutorial, you will build a web application where the end user will be able to navigate to an area of interest and press a Print button. The output will be a printer-friendly PDF document containing vector output for service layers.

Exporting from a web application using the out-of-the-box Printing Tools service and its client print widgets in the ArcGIS Web APIs can output a high-quality image of your service layers. However, sometimes vector output is desired. For example, vector PDF output supports the following in PDF viewing applications: toggling layer visibility, viewing of feature attributes, and viewing of map coordinates. In the web application that you will build, you will output vector equivalents of the World Topographic basemap service. A vector subset of a small area of the World Topographic basemap service is publically available on ArcGIS Resources.

NoteNote:

In order to generate an output document containing local vector data instead of an image of your service layers, you must have access to the corresponding vector data.

The illustration below shows the web application you will build.

Web application

The code behind the Print button in the web application uses the ArcGIS Web API Print Task, which is available starting with ArcGIS 10.1 for Server. You will create a Python script that will be published as a geoprocessing service that the Print Task will use. The Python script uses the ConvertWebMapToMapDocument function in the arcpy.mapping module which will insert the full state of the web map into a staged template map document. The template contains vector equivalents of all the possible layers in the map service. The arcpy.mapping module also provides functionality to remove service layers—leaving local vector data that was staged in the template map document—and export to PDF.

After clicking the Print button, the output will be a printer-friendly PDF document containing local vector data instead of an image of service layers.

To complete this tutorial, you should be familiar with the arcpy.mapping module, the ArcGIS Web APIs, ArcGIS for Desktop, and ArcGIS for Server. Sample code for all the ArcGIS Web APIs are included in this tutorial. You should also become familiar with printing in web application help topics:

Printing in web applicationsAdvanced printing for web maps

Get the vector data from the ArcGIS Resource Center

You will download a zip file from the Community Maps Template Gallery on ArcGIS Resources that contains the vector data used in the staged map document template.

Before you do that, you need to make sure that a folder structure exists where ArcGIS for Server can see the template map document and data that will be used in the web application. This tutorial assumes you have a folder that is registered with ArcGIS for Server. For more information on registering data with ArcGIS for Server, see

TipTip:

When using template map documents in the ConvertWebMapToMapDocument function, the best practice is to use data registered with ArcGIS for Server. If you choose to not use registered data, template map documents and data will be packaged and copied to the server. During packaging, data may be moved and resourced with relative paths to a folder structure that ConvertWebMapToMapDocument cannot resolve. For more information, see ConvertWebMapToMapDocument.

Steps:
  1. Open a new, empty ArcMap session.
  2. In the Catalog window, browse to your registered folder. Create a new folder in the registered folder named BasicTutorial.
  3. Go to the Community Maps Template Gallery on ArcGIS Resources. Select the World Topographic Map Template.
  4. Download the World Topographic Map Template zip file to your computer by clicking the Download button.
  5. Extract the contents of the zip file to the BasicTutorial folder in your registered folder.
  6. Your registered folder should look similar to the illustration below.
    Registered folder
    NoteNote:

    In the screen capture above, the registered folder is named MyDataStore. Your registered folder can be named anything.

The LGIM_World_Topo_Map_v1.5 map document is now ready to be used in the web application.

Create the Python script

You will create a Python script that will be used as the custom geoprocessing print service.

The Python script in the custom geoprocessing print service executes the ConvertWebMapToMapDocument function which converts a web map (in JSON format) that you intend to print or export to a map document. The script then removes the service layers in the output map document, leaving the vector layers that correspond to the service layer in the web map JSON. The web map JSON also contains the extent of the map in the web application. Finally, the script exports the map document to a PDF document.

Steps:
  1. Open any Python IDE, such as IDLE (which comes with ArcGIS for Desktop).
  2. Copy and paste the following code into a new Python script.
  3. import arcpy
    import os
    import uuid
    
    # Input WebMap json
    Web_Map_as_JSON = arcpy.GetParameterAsText(0)
    
    # The template location in the server data store
    templateMxd = '//MyComputer/MyDataStore/BasicTutorial/LG_062912_10.1/LG_062912_10.1/LGIM_World_Topo_Map_v1.5.mxd'
       
    # Convert the WebMap to a map document
    result = arcpy.mapping.ConvertWebMapToMapDocument(Web_Map_as_JSON, templateMxd)
    mxd = result.mapDocument
    
    # Reference the data frame that contains the webmap
    # Note: ConvertWebMapToMapDocument renames the active dataframe in the template_mxd to "Webmap"
    df = arcpy.mapping.ListDataFrames(mxd, 'Webmap')[0]
    
    # Remove the service layer
    # This will just leave the vector layers from the template
    for lyr in arcpy.mapping.ListLayers(mxd, data_frame=df):
        if lyr.isServiceLayer:
            arcpy.mapping.RemoveLayer(df, lyr)
            
    # Use the uuid module to generate a GUID as part of the output name
    # This will ensure a unique output name
    output = 'WebMap_{}.pdf'.format(str(uuid.uuid1()))
    Output_File = os.path.join(arcpy.env.scratchFolder, output)
    
    # Export the WebMap
    arcpy.mapping.ExportToPDF(mxd, Output_File) 
    
    # Set the output parameter to be the output file of the server job
    arcpy.SetParameterAsText(1, Output_File)
    
    # Clean up - delete the map document reference
    filePath = mxd.filePath
    del mxd, result
    os.remove(filePath)
    
  4. Change the templateMxd variable to be the UNC path to the folder in your registered folder that contains the template map documents.
  5. NoteNote:

    If the ArcGIS for Server, ArcGIS for Desktop, and the registered folder are all on the same machine, the UNC paths to the registered folder are not required. Instead, absolute paths can be used.

  6. Save the Python script. Name the script BasicTutorial.py. Save it in a folder named WebApp within the registered folder.

Create a Python script tool

You will create a custom geoprocessing tool that runs the BasicTutorial.py script.

Steps:
  1. In the Catalog window in ArcMap, browse to the WebApp folder in your registered folder directory.
  2. Right-click the WebApp folder and click New > Toolbox. Name the toolbox BasicTutorial.
  3. Right-click the BasicTutorial toolbox and click Item Description.
  4. In the Item Description dialog box, populate in the Tags and Summary items with the text of your choice. Optionally, fill in other item descriptions.
  5. Click Save and exit the Item Description dialog box.
  6. In the Catalog window, right-click the BasicTutorial toolbox and click Add > Script.
  7. In the Add Script dialog box, enter BasicTutorial for both the Name and the Label.
  8. Click Next.
  9. For the Script File, browse to the WebApp folder in the registered folder and select BasicTutorial.py.
  10. Click Next.
  11. Two parameters need to be added to the script tool.
    1. The first one will be Web_Map_as_JSON. This parameter takes in a JSON representation of the state of the map to be exported as it appears in the web application. The properties should match the following:
      Web_Map_as_JSON parameter properties
    2. The next parameter is Output_File—the output file that will be created. The properties should match the following:
      Output_File parameter properties

      CautionCaution:

      The Web_Map_as_JSON and Output_File parameter names must be spelled exactly as shown so they match the tool signature of the Print Task in the ArcGIS Web APIs.

    3. Click Finish on the Add Script dialog box.
  12. Right-click the BasicTutorial script tool and click Item Description.
  13. In the Item Description dialog box, populate the Tags and Summary items with the text of your choice. Also populate the Dialog Explanation for all four parameters in the Syntax section of the Item Description dialog box with the text of your choice. Optionally, fill in other item descriptions.

Execute the tool

The tool needs to be executed successfully to create a result in the Results window that can be published to ArcGIS for Server.

Steps:
  1. In the Catalog window, right-click the BasicTutorial script tool and click Open.
  2. Leave the Web_Map_as_JSON input parameter blank.
    TipTip:

    For publishing purposes, you can leave the Web_Map_as_JSON input parameter blank as the ArcGIS Web APIs will provide the web map JSON in the web application. You can leave the Web_Map_as_JSON input parameter blank provided the Python script was written in such a way as to not fail with blank input. For example, the script doesn't look for web map layers by name.

  3. Click OK. Wait for the tool to finish executing.

The result is now ready to be published as a geoprocessing service.

Publish the result

If you are unfamiliar with publishing geoprocessing services, see

Steps:
  1. Open the Results window.
  2. Expand Current Session.
  3. Right-click the BasicTutorial result and click Share As > Geoprocessing Service.
  4. Check Publish a service.
  5. Click Next.
  6. Choose a publish or admin connection to your ArcGIS for Server machine.
  7. Click Next.
  8. Click Next.
  9. Click Continue.
  10. In the upper right corner of the Service Editor dialog box, click Publish.

The geoprocessing service is now ready to be used in the ArcGIS Web APIs.

Choose which ArcGIS Web API you want to use to create the web application

The ArcGIS Web APIs offer similar GIS functionality; it is up to you to choose your preferred developer platform. It is beyond the scope of this tutorial to go into detail about getting started building web applications with each API. Consult the API documentation for that. For more information, see

Each ArcGIS Web API has a Print Task. The Print Task has a URL property that will reference the custom geoprocessing service you created earlier. Consult the ArcGIS Web API documentation for more information on the Print Task.

In the sections below, use the code samples for the ArcGIS Web API that you work in to build your web application.

ArcGIS API for JavaScript sample code

If you are using the ArcGIS API for JavaScript, use the following sample code to build your web application.

In the ArcGIS API for JavaScript code sample below, change the URL to the geoprocessing service you created in a previous step to match your server name. It is referenced on this line:

var printUrl = "http://MyServer:6080/arcgis/rest/services/BasicTutorial/GPServer/BasicTutorial";

Code for BasicTutorial.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
  	<title>Webmap Printing</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=7,IE=9" />
    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
    <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/3.0/js/dojo/dijit/themes/claro/claro.css">  
    <script src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=3.0"></script>
    <script type="text/javascript" language="Javascript">
      dojo.require("esri.map");
      dojo.require("esri.tasks.PrintTask");
      var printTask, params;
      
      function init() {
		      // set the extent to the Naperville area - which matches the extent of the template map document
        var startExtent = new esri.geometry.Extent({"xmin":-9815098,"ymin":5124020, "xmax":-9811168,"ymax":5127339, "spatialReference":{"wkid":102100}});
        											
        var map = new esri.Map("map", {extent:startExtent});
        
        // add tiled map service to webmap
        var tiledUrl = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer";
        var tiledLayer = new esri.layers.ArcGISTiledMapServiceLayer(tiledUrl);
        map.addLayer(tiledLayer);
		      var printUrl = "http://MyServer:6080/arcgis/rest/services/BasicTutorial/GPServer/BasicTutorial";
		      printTask = new esri.tasks.PrintTask(printUrl, {async: true});
		      params = new esri.tasks.PrintParameters();
        params.map = map;  
      }
      
      function print(){
      	var ptemplate = new esri.tasks.PrintTemplate();
      	// use the extent of the webmap in the output PDF
      	ptemplate.preserveScale = false;
      	params.template = ptemplate;
        printTask.execute(params, printComplete);
      }
      
      function printComplete(result){
        window.open(result.url);
      }

      dojo.addOnLoad(init);

    </script>
  </head>
  <body class="claro">
  	 <input type="button" id="print" value="Print" onclick="print();"/>
    <div id="map" style="width:1000px; height:600px; border:1px solid #000;"></div>
  </body>
</html>

ArcGIS API for Flex sample code

If you are using ArcGIS API for Flex, use the following sample code to build your web application.

In the ArcGIS API for Flex code sample below, change the URL to the geoprocessing service you created in a previous step to match your server name. It is referenced on these lines:

url="http://MyServer:6080/arcgis/rest/services/BasicTutorial/GPServer/BasicTutorial"/>

Code for BasicTutorial.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:esri="http://www.esri.com/2008/ags"
			   initialize="printTask.getServiceInfo()"
			   pageTitle="High-quality printing">
	
	<fx:Script>
		<![CDATA[
			import com.esri.ags.events.PrintEvent;
			import com.esri.ags.tasks.supportClasses.DataFile;
			import com.esri.ags.tasks.supportClasses.JobInfo;
			import com.esri.ags.tasks.supportClasses.ParameterValue;
			
			import mx.controls.Alert;
			
			public var preserveScale:Boolean = true;
			
			private function printBtn_clickHandler(event:MouseEvent):void
			{
				if (printTask.getServiceInfoLastResult.isServiceAsynchronous)
				{
					printTask.submitJob(printParameters);
				}
				else
				{
					printTask.execute(printParameters);
				}
			}
			
			private function printTask_jobCompleteHandler(event:PrintEvent):void
			{
				var jobInfo:JobInfo = event.jobInfo;
				if (jobInfo.jobStatus == JobInfo.STATUS_SUCCEEDED)
				{
					printTask.getResultData(jobInfo.jobId);
				}
				else
				{
					Alert.show(jobInfo.jobStatus);
				}
			}
			
			private function printTask_getResultDataCompleteHandler(event:PrintEvent):void
			{
				var dataFile:DataFile = event.parameterValue.value as DataFile;
				navigateToURL(new URLRequest(dataFile.url));
			}
			
			private function printTask_executeCompleteHandler(event:PrintEvent):void
			{
				var paramValue:ParameterValue = event.executeResult.results[0];
				var dataFile:DataFile = paramValue.value as DataFile;
				navigateToURL(new URLRequest(dataFile.url));
			}
		]]>
	</fx:Script>
	
	<fx:Declarations>
		<esri:PrintTask id="printTask"
						executeComplete="printTask_executeCompleteHandler(event)"
						fault="Alert.show(event.fault.faultString)"
						getResultDataComplete="printTask_getResultDataCompleteHandler(event)"
						jobComplete="printTask_jobCompleteHandler(event)"
						showBusyCursor="false"
						url="http://MyServer:6080/arcgis/rest/services/BasicTutorial/GPServer/BasicTutorial"/>
		<esri:PrintParameters id="printParameters"
							  preserveScale="false"
							  map="{map}">
		</esri:PrintParameters>
		
		<esri:Extent id="initialExtent"
					 xmin="-9815098" ymin="5124020" xmax="-9811168" ymax="5127339">
			<esri:SpatialReference wkid="102100"/>
		</esri:Extent>
		
	</fx:Declarations>
	
	<s:controlBarLayout>
		<s:HorizontalLayout gap="10"
							paddingBottom="7"
							paddingLeft="10"
							paddingRight="10"
							paddingTop="7"
							verticalAlign="baseline"/>
	</s:controlBarLayout>
	<s:controlBarContent>
		<s:Button id="printBtn"
				  click="printBtn_clickHandler(event)"
				  enabled="{printTask.getServiceInfoLastResult != null}"
				  label="Print"/>
	</s:controlBarContent>

	<esri:Map id="map" extent="{initialExtent}">
		
		<esri:ArcGISTiledMapServiceLayer url="http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"/>
	</esri:Map>
	
</s:Application>

ArcGIS API for Silverlight sample code

If you are using ArcGIS API for Silverlight, use the following sample code to build your web application.

XAML code

<UserControl x:Class="Basic_Tutorial.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:esri="http://schemas.esri.com/arcgis/client/2009">

    <Grid x:Name="LayoutRoot" Background="White">

        <esri:Map x:Name="MyMap" Extent="-9815098,5124020,-9811168,5127339">
            <esri:Map.Layers>
                <esri:ArcGISTiledMapServiceLayer ID="MyTiledService" 
                    Url="http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"/>
            </esri:Map.Layers>
        </esri:Map>

        <StackPanel Orientation="Vertical" Margin="15" HorizontalAlignment="Left" VerticalAlignment="Top" >
            <Border x:Name="PrintPanel" CornerRadius="10" BorderBrush="Gray" BorderThickness="1" Background="White" HorizontalAlignment="Left" 
                    VerticalAlignment="Top" Margin="15" Padding="10">
                <Border.Effect>
                    <DropShadowEffect/>
                </Border.Effect>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Button x:Name="Print" Content="Print" Click="ExportMap_Click" 
                            Grid.Row="1" Grid.Column="0" Margin="2"/>
                </Grid>
            </Border>
        </StackPanel>
    </Grid>
</UserControl>

In the ArcGIS API for Silverlight code sample below, change the URL to the geoprocessing service you created in a previous step to match your server name. It is referenced on this line:

printTask = new PrintTask("http://MyServer:6080/arcgis/rest/services/BasicTutorial/GPServer/BasicTutorial");

Code behind in C#

using System;
using System.Windows;
using System.Windows.Controls;
using ESRI.ArcGIS.Client.Printing;
using ESRI.ArcGIS.Client.Tasks;


namespace Basic_Tutorial
{
    public partial class MainPage : UserControl
    {
        PrintTask printTask;
        public MainPage()
        {
            InitializeComponent();
            printTask = new PrintTask("http://MyServer:6080/arcgis/rest/services/BasicTutorial/GPServer/BasicTutorial");
            printTask.DisableClientCaching = true;
            printTask.JobCompleted += new EventHandler<PrintJobEventArgs>(printTask_JobCompleted);
            printTask.GetServiceInfoCompleted += new EventHandler<ServiceInfoEventArgs>(printTask_GetServiceInfoCompleted);
            printTask.GetServiceInfoAsync();
        }

        void printTask_GetServiceInfoCompleted(object sender, ServiceInfoEventArgs e)
        {
            PrintPanel.DataContext = e.ServiceInfo;
        }


        void printTask_JobCompleted(object sender, PrintJobEventArgs e)
        {
            if (e.Error == null)
            {
                if (e.PrintResult != null)
                    System.Windows.Browser.HtmlPage.Window.Navigate(e.PrintResult.Url, "_blank");
            }
        }

        private void ExportMap_Click(object sender, RoutedEventArgs e)
        {

            if (printTask == null || printTask.IsBusy) return;

            PrintParameters printParameters = new PrintParameters(MyMap)
            {

            };
            printTask.SubmitJobAsync(printParameters);
        }
    }
}

Run the web application

Run the web application you created in the previous step. Consult the ArcGIS Web API documentation for instructions on running web applications, if necessary. A screen capture of the ArcGIS for JavaScript web API application is below.

Sample web application

Zoom in to an area of interest and click the Print button. The output PDF document will automatically pop up after a moment. The output will be a printer-friendly PDF document containing local vector data that was staged in the layout templates instead of an image of service layers. See below for sample output:

Sample PDF output

This concludes basic web map printing and exporting using the arcpy.mapping tutorial. For more advanced scenarios, see Advanced web map printing and exporting using arcpy.mapping tutorial.

Related Topics

9/1/2015