教程:使用 arcpy.mapping 进行基本的高质量 Web 地图打印/导出
在本教程中,您将构建 Web 应用程序,使最终用户能够导航至感兴趣区域并按住打印按钮以进行打印。输出结果为易于打印的 PDF 文档,包含了高质量服务图层的矢量输出。
默认情况下,使用 ArcGIS Web API 中开箱即用的打印工具服务及其客户端打印微件从 Web 应用程序中导出服务图层的图像,该图像的分辨率可能过低(如 96 dpi),无法进行大幅面打印或高质量制图输出。服务图层使用切片地图缓存时更是如此。在此情况下,服务图层的输出将成为切片缓存图像。
下图显示了两个输出 PDF 文件的示例。左侧图像显示了导出至大幅面 PDF 并进行放大时,服务图层切片缓存可能的外观。请注意要素和像素化文本的模糊度。当以较小比例或缓存比例查看时会更清晰。以信纸大小或文摘大小查看也可以,但大小不能更大了。
右侧的图像显示了矢量数据。该图像更加平滑,也更适合高质量制图输出。本教程将介绍如何自定义开箱即用的打印方案以输出易于打印的 PDF 文档,该文档包括了高质量服务图层的矢量输出。
在待构建的 Web 应用程序中,您会输出世界地形图底图服务的矢量等效表示。世界地形图底图服务的矢量子集可在 ArcGIS Resource Center 上找到。
要生成含有高质量本地矢量数据的输出文档,而非服务图层的切片缓存图像,您必须具有访问矢量数据的权限。
下图显示了将要构建的 Web 应用程序。
Web 应用程序中打印按钮后的代码使用了 ArcGIS Web API 的打印任务,该任务可用于 ArcGIS 10.1 for Server。您将创建可发布为地理处理服务的 Python 脚本,该地理处理服务可被打印任务所使用。Python 脚本使用 arcpy.mapping 模块中的 ConvertWebMapToMapDocument 函数,该函数会将 web 地图的完整状态插入过渡的模板地图文档中。模板包含了地图服务中所有可能图层的矢量等效表示。arcpy.mapping 模块还可用于移除服务图层,同时将已过渡的本地矢量数据保留在模板地图文档中并导出至 PDF。
单击打印按钮后,输出结果为易于打印的 PDF 文档,该文档包含了高质量的本地矢量数据而非服务图层的切片缓存图像。
要完成本教程,应熟悉 arcpy.mapping 模块、ArcGIS Web API、ArcGIS for Desktop 和 ArcGIS 10.1 for Server。本教程包含了所有 ArcGIS Web API 的示例代码。您还应逐渐熟悉 web 应用程序帮助主题中的打印:
从 ArcGIS Resource Center 获取矢量数据
您将从 ArcGIS Resource Center 上的地图模板库中下载 zip 文件,其中包含已过渡地图文档模板中使用的矢量数据。
在此之前,需要确保存在一个文件夹结构以便 ArcGIS for Server 可以查看将用于 web 应用程序的模板地图文档和数据。本教程假定存在注册到 ArcGIS for Server 的文件夹。有关将数据注册到 ArcGIS for Server 的更多信息,请参阅:
在 ConvertWebMapToMapDocument 函数中使用模板地图文档时,最好的做法是使用注册到 ArcGIS for Server 的数据。如果选择不使用注册的数据,那么模板地图文档和数据将打包复制到服务器。在打包过程中, 数据可能被移动并以相对路径重新指向 ConvertWebMapToMapDocument 无法解析的文件夹结构。有关详细信息,请参阅 ConvertWebMapToMapDocument 帮助主题。
- 打开新的空 ArcMap 会话。
- 在目录 窗口中,导航至已注册文件夹。在已注册文件夹中创建一个名为 BasicTutorial 的新文件夹。
- 转至 ArcGIS Resource Center 上的地图模板库。选择地形图模板 - 中比例尺模板。
- 通过单击下载按钮,下载地形图模板 - 中比例尺 zip 文件至您的计算机。
- 提取 zip 文件的内容至已注册文件夹中的 BasicTutorial 文件夹。
- 已注册文件夹应如下图所示。注:
在上方的屏幕截图中,已注册文件夹名为 MyDataStore。可随意设置已注册文件夹的名称。
准备在 Web 应用程序中将地图文档用作模板
从 ArcGIS Resource Center 下载的数据包含了两个地图文档。MediumScales_anno.mxd 将用在 Web 应用程序中。例如,地图和数据在约为 1:1,500,000 到 1:18,000 的中比例尺下使用。请移除地图文档中的最大比例范围,以便 Web 应用程序用户可将地图或数据放大到超过最大比例且仍然能生成打印输出。
- 在 ArcMap 中打开 MediumScales_anno.mxd。
- 在内容列表中右键单击级别 14 ~1:36K 图层组,然后单击属性。
- 单击常规选项卡。
- 将放大超过限值时不显示图层下拉列表设置为 <无>。
- 单击图层组属性 对话框中的确定。
- 保存地图文档。
地图文档现已准备就绪,可以在 Web 应用程序中使用。
创建 Python 脚本
您将创建用作自定义地理处理打印服务的 Python 脚本。
自定义地理处理打印服务中的 Python 脚本会执行 ConvertWebMapToMapDocument 功能,可转换待打印或导出至地图文档的 Web 地图(以 JSON 格式)。该脚本随后会移除输出地图文档中的服务图层,保留与 Web 地图 JSON 中服务图层相对应的矢量图层。Web 地图 JSON 还包含了 Web 应用程序中的地图范围。最后,该脚本将地图文档导出至 PDF。
- 打开任意的 Python IDE,如 IDLE(ArcGIS for Desktop 随附文件)。
- 将以下代码复制并粘贴到新的 Python 脚本中。
- 将 templateMxd 变量更改为包含模板地图文档的已注册文件夹中的文件夹 UNC 路径。
- 保存 Python 脚本。命名脚本 BasicTutorial.py。将其保存至已注册文件夹中名为 WebApp 的文件夹内。
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 = '//zoid/MyDataStore/BasicTutorial/Maps and GDBs/MediumScales_Anno.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)
如果 ArcGIS for Server、ArcGIS for Desktop 和已注册文件夹都在同一台计算机上,则无需已注册文件夹的 UNC 路径。可使用绝对路径代替。
创建 Python 脚本工具
您将创建运行 BasicTutorial.py 脚本的自定义地理处理工具。
- 在 ArcMap 的目录 窗口中,导航至已注册文件夹目录中的 WebApp 文件夹。
- 右键单击 WebApp 文件夹并单击新建 > 工具箱。将该工具箱命名为 BasicTutorial。
- 右键单击 BasicTutorial 工具箱并单击项目描述。
- 在项目描述 对话框中,使用所选文本填充标签 和汇总 项目。或者,填写其他项目描述。
- 单击保存 并退出项目描述 对话框。
- 在目录 窗口中,右键单击 BasicTutorial 工具箱并单击添加 > 脚本。
- 在添加脚本 对话框中,为名称和标注输入 BasicTutorial。
- 单击下一步。
- 对于脚本文件,导航至已注册文件夹中的 WebApp 文件夹并选择 BasicTutorial.py。
- 单击下一步。
- 两个参数需要添加至脚本工具。
- 第一个参数为 Web_Map_as_JSON。该参数采用地图在 Web 应用程序中显示时导出的地图状态的 JSON 表示。属性应与以下屏幕截图相匹配:
- 另一参数为 Output_File,即将创建的输出文件。属性应与以下屏幕截图相匹配:警告:
Web_Map_as_JSON 和 Output_File 参数名称必须完全按照以下所示拼写,以便与 ArcGIS Web API 中打印任务的工具签名相匹配。
- 单击添加脚本 对话框上的完成。
- 右键单击 BasicTutorial 脚本工具并单击项目描述。
- 在项目描述 对话框中,使用所选文本填充标签和汇总项目。同样,使用所选文本填充项目描述 对话框语法部分内所有四个参数的对话框说明。或者,填写其他项目描述。
执行工具
需要成功执行工具以在结果 窗口中创建可发布到 ArcGIS for Server 中的结果。
- 在目录 窗口中,右键单击 BasicTutorial 脚本工具并单击打开。
- 将 Web_Map_as_JSON 输入参数留空。提示:
出于发布目的,由于 ArcGIS Web API 在 Web 应用程序中提供 Web 地图 JSON,您可将 Web_Map_as_JSON 输入参数留空。如果 Python 脚本的写入方式使其在空白输入时仍能正常执行,则可将 Web_Map_as_JSON 输入参数留空。例如,脚本不会按名称查找 Web 地图图层。
- 单击确定。等待工具完成执行。
结果现已准备就绪,可作为地理处理服务发布。
- 打开结果 窗口。
- 展开当前会话。
- 右键单击 BasicTutorial 结果并单击共享为 > 地理处理服务。
- 检查发布服务。
- 单击下一步。
- 选择 ArcGIS 10.1 for Server 服务器的发布或管理连接。
- 单击下一步。
- 单击下一步。
- 单击继续。
- 在服务编辑器 对话框的右上角,单击发布。
地理处理服务现已准备就绪,可以在 ArcGIS Web API 中使用。
选择想要用来创建 web 应用程序的 ArcGIS web API。
ArcGIS web APIs 提供类似的 GIS 功能;您需要选择您偏好的开发平台。本教程不包含关于利用每种 API 开始构建 web 应用程序的详细介绍。有关此介绍的详细信息,请参阅 API 文档。有关详细信息,请参阅:
每个 ArcGIS Web API 都拥有打印任务。打印任务具有一个引用之前所创建的自定义地理处理服务的 URL 属性。有关打印任务的详细信息,请参阅 ArcGIS web API 文档。
在以下部分中,使用用来构建 Web 应用程序的 ArcGIS Web API 的代码示例。
ArcGIS API for JavaScript 示例代码
如果您正在使用 ArcGIS API for JavaScript,那么请使用以下示例代码来构建 web 应用程序。
在以下 ArcGIS API for JavaScript 代码示例中,将 URL 更改为在上一步骤中创建的地理处理服务,以匹配服务器名称。在本行中进行引用:
var printUrl = "http://MyServer:6080/arcgis/rest/services/BasicTutorial/GPServer/BasicTutorial";
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() {
// limit scales in webmap to the usable scales of the template map document
var lods = [ {"level" : 11, "resolution" : 76.4370282850732, "scale" : 288895.277144},
{"level" : 12, "resolution" : 38.2185141425366, "scale" : 144447.638572},
{"level" : 13, "resolution" : 19.1092570712683, "scale" : 72223.819286},
{"level" : 14, "resolution" : 9.55462853563415, "scale" : 36111.909643},
{"level" : 15, "resolution" : 4.77731426794937, "scale" : 18055.954822}];
// set the extent to the Portland area - which matches the extent of the template map document
var startExtent = new esri.geometry.Extent({"xmin":-13679000,"ymin":5680000,
"xmax":-13635000,"ymax":5740000,
"spatialReference":{"wkid":102100}});
var map = new esri.Map("map", {extent:startExtent, lods:lods});
// 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 示例代码
如果您正在使用 ArcGIS API for Flex,请使用以下示例代码构建 Web 应用程序。
在以下 ArcGIS API for Flex 代码示例中,将 URL 更改为在上一步骤中创建的地理处理服务,以匹配服务器名称。在这些行中进行引用:
url="http://MyServer:6080/arcgis/rest/services/BasicTutorial/GPServer/BasicTutorial"/>
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="-13679000" ymin="5680000" xmax="-13635000" ymax="5740000">
<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:lods>
<esri:LOD resolution="76.4370282850732" scale="288895.277144"/>
<esri:LOD resolution="38.2185141425366" scale="144447.638572"/>
<esri:LOD resolution="19.1092570712683" scale="72223.819286"/>
<esri:LOD resolution="9.55462853563415" scale="36111.909643"/>
<esri:LOD resolution="4.77731426794937" scale="18055.954822"/>
</esri:lods>
<esri:ArcGISTiledMapServiceLayer url="http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"/>
</esri:Map>
</s:Application>
ArcGIS API for Silverlight 示例代码
如果您正在使用 ArcGIS API for Silverlight,请使用以下示例代码构建 Web 应用程序。
XAML 代码:
<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="-13679000,5680000,-13635000,5740000">
<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>
在以下 ArcGIS API for Silverlight 代码示例中,将 URL 更改为在上一步骤中创建的地理处理服务,以匹配服务器名称。在本行中进行引用:
printTask = new PrintTask("http://MyServer:6080/arcgis/rest/services/BasicTutorial/GPServer/BasicTutorial");
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);
}
}
}
运行 Web 应用程序
运行在上一步骤中创建的 Web 应用程序。如有必要,请参阅 ArcGIS Web API 文档以获取运行 Web 应用程序的说明。ArcGIS for JavaScript Web API 应用程序的屏幕截图如下。
放大至感兴趣区域并单击打印按钮。稍后,将自动弹出输出 PDF。输出结果为易于打印的 PDF 文档,包含了在布局模板中分段的高质量本地矢量数据,而非服务图层的切片缓存图像。这会生成高质量的制图输出。请参阅如下示例输出:
这包括了基本高质量 Web 地图打印/导出教程。有关更多高级方案,请参阅高级高质量 Web 地图打印/导出教程。