使用 arcpy.mapping 更新和修复数据源
在许多情况下,您都可能需要修复数据源或重定向数据源至其他位置。然而,如果是在每个相关的地图文档中手动进行更改,则会显得异常麻烦。arcpy.mapping 脚本环境提供了多种方法使得您无需打开地图文档即可自动进行更改。您可针对各个图层逐一更新数据源,也可一次更新同一工作空间中的所有图层。各方法的帮助主题都提供了简要介绍,而此文档的目的在于对这些方法进行概括和比较。
实际上,基本存在两种不同类型的方法用于处理 MapDocument、Layer 和 TableView 类。方法名称会根据可用的类和参数的不同而稍有变化。
- findAndReplaceWorkspacePath 或 findAndReplaceWorkspacePaths 允许您替换图层或表工作空间路径的全部或部分字符串。如果工作空间类型或数据集名称已更改,则不能使用此方法。使用此方法的理想情境是盘符更改、切换至 UNC 路径、更新 SDE 连接文件信息等等。请参阅以下的“通常情况”部分。
- replaceWorkspaces 或 replaceDataSource 允许您更改工作空间路径、工作空间类型和/或更改数据集名称。
这些方法的功能相互间互有重复;因此,可通过多种方法更新图层或表的工作空间或数据集。此文档的目的在于讨论所有方法并针对许多常见情景提供最佳操作建议。
以下是本文档中所使用的一些术语的定义:
- 工作空间是一种数据容器。工作空间可以是包含 shapefile、CAD 文件或栅格数据等内容的文件夹,也可以是 ArcInfo coverage、个人地理数据库、文件地理数据库和 SDE 连接等。
- 工作空间路径是指工作空间的系统路径。对于基于文件的数据(如 CAD、shapefile 或栅格),工作空间路径包括了该数据所在的文件夹的名称。对于基于文件的地理数据库,工作空间路径包括地理数据库的名称。SDE 工作空间路径由 SDE 连接文件的系统路径定义。
- 工作空间类型是 ArcGIS 支持的数据模型格式。例如,shapefile、个人地理数据库、文件地理数据库、CAD、SDE 等等。
- 数据集是工作空间中的要素类或表。数据集的名称是工作空间中此对象的名称,而不是地图文档的内容列表中所显示的样式。
- 图层或表的数据源是工作空间和数据集的组合。
用于更新和修复数据源的方法
- MapDocument.findAndReplaceWorkspacePaths(find_workspace_path, replace_workspace_path, {validate})
- MapDocument 对象的 findAndReplaceWorkspacePaths 方法专用于在共享某工作空间路径的地图文档中执行全局查找并替换所有图层和表的工作空间路径。此方法可一次替换多种工作空间类型的路径(例如,CAD 文件、shapefile 和文件地理数据库)。
- MapDocument.replaceWorkspaces(old_workspace_path, old_workspace_type, new_workspace_path, new_workspace_type, {validate})
- MapDocument 对象的 replaceWorkspaces 方法与 findAndReplaceWorkspacePaths 方法类似,但是前者还允许用户从一种工作空间类型切换至另一工作空间类型(例如,从个人地理数据库切换至文件地理数据库)。通常,此方法一次只处理一种工作空间类型。如果需要替换多种工作空间类型,则只需针对每种工作空间类型重复调用此方法即可。有关其他用法,请参阅下面的“一般用法说明”。
- Layer.findAndReplaceWorkspacePath(find_workspace_path, replace_workspace_path, {validate})
- Layer 对象的 findAndReplaceWorkspacePath 方法专用于在地图文档或图层文件中执行查找并替换单个图层的工作空间路径。
- Layer.replaceDataSource(workspace_path, workspace_type, dataset_name, {validate})
- Layer 对象的 replaceDataSource 方法与 findAndReplaceWorkspacePath 方法类似,但除了可更改工作空间类型外,前者也可更改数据集。这是在工作空间内替换图层数据集的唯一方法。此方法在新的工作空间中数据集名称发生变化的情况下尤为有用。
- TableView.findAndReplaceWorkspacePath(find_workspace_path, replace_workspace_path, {validate})
- TableView 对象的 findAndReplaceWorkspacePath 方法专用于在地图文档中执行查找并替换单个表的工作空间路径。
- TableView.replaceDataSource(workspace_path, workspace_type, dataset_name, {validate})
- TableView 对象的 replaceDataSource 方法与 findAndReplaceWorkspacePath 方法类似,但除了可更改工作空间类型外,该方法也可以更改数据集。这是在工作空间内替换表数据集的唯一方法。此方法在新的工作空间中数据集名称发生更改的情况下尤为有用。
一般用法说明
- ListBrokenDataSources() 函数对于确定地图文档或图层文件中损坏的图层或表很有用。
- 这些方法对所有图层和表进行递归处理。这意味着,还会搜索和更新连接表和关联表(请参阅下面的“已知局限性”部分)。
- 可以查找和替换整个工作空间路径或其中的一部分。如果仅替换部分路径,则需确保它是唯一字符串。例如,对于驱动器而言,仅查找字母 C 并将其替换为字母 D 的操作结果不如查找 r"C:\" 并替换为 r"D:\" 的操作结果更加明确。
- 使用图层名称区分数据源并非完全可靠,因为可能存在多个数据源具有相同图层名称的情况。可能需要使用其他图层属性来唯一地区分图层。
- 不要在工作空间路径中包含地理数据库要素数据集的名称。要素数据集是工作空间的组成部分。例如,如果将要素类从独立要素类移到地理数据库要素数据集中,则地图文档会忽略损坏图层而正常打开。
- 工作空间路径的参数值中不应包括图层或表的数据集名称;因此,如果图层或表的数据集名称发生更改,则上述许多方法将不可用。Layer 和 TableView 对象的 replaceDataSource 方法即可应对此种情况。
- 通过 replaceWorkspaces 方法切换工作空间时,数据集名称必须相同。例如,对于名称为 Highways.shp 的 shapefile,只有在文件地理数据库中数据集的名称也为 Highways 时,才可以将其重定向至该文件地理数据库工作空间。如果数据集名称不同,则需要调用 Layer 或 TableView 对象的 replaceDataSource 方法。
- 如果对 old_workspace_path 参数赋予空字符串 ("") 并对 old_workspace_type 参数赋予 "NONE" 值,则所有数据源都与新的工作空间相匹配。这样,您通过一种方法即可将多个工作空间重定向到一个工作空间。空字符串只能与关键字 NONE 结合使用。
可选参数 validate
- 此参数允许您在更改或替换图层或表的数据源之前测试新数据源是否存在。
- 如果 validate 为 True 并且新数据源存在且有效,则原始数据源将更新为新数据源;否则将继续指向原始数据源并不做更新。
- 如果 validate 为 False,则新数据源不必有效(即,它可能尚不存在)。这对尚未创建数据时需要更新数据源的情况十分有用。请注意,在上述情况下,数据在关联的地图文档或图层文件中将显示为已损坏。
- 使用 findAndReplaceWorkspacePath 或 findAndReplaceWorkspacePaths 方法时,validate 参数应用于 replace_workspace_path 参数。
- 使用 replaceWorkspacePaths 方法时,validate 参数应用于 new_workspace_path 和 new_workspace_type 参数。
- 使用 replaceDataSource 方法时,validate 参数应用于 dataset_name 参数。
使用 SDE 连接
- SDE 连接的工作空间路径是 SDE 连接 (.sde) 文件的路径。您可以提供文件的完整路径,如果连接文件位于数据库连接文件夹中,也可以在路径中使用该字符串,例如 find_workspace_path=r"Database Connections\myConnectionFile.sde"
- 脚本中使用的 SDE 连接文件信息必须与用来将数据添加到地图文档或图层文件的 SDE 连接相同。例如,两个不同的位置可以具有两个相同的 SDE 连接。如果已使用第一个连接文件路径将数据添加到地图中,但在脚本中使用的是第二个连接文件路径,则数据源将不会按预期进行更新。
- 当从 SDE 连接移除密码信息以便使其在地图文档或图层文件中不再保留时,需要设置 validate=False。因为如果试图使用不保留所有正确连接信息的 SDE 连接文件,则显然连接会失败,所以必须执行此操作。设置 validate=False 将强制使用新连接文件,当用户打开包含 SDE 数据的地图文档时系统会提示输入登录信息。
已知局限性
- 由于逻辑示意图数据集设计中包含隐藏表,因此无法对其更新。
- 目前不支持直接与 SQL Server Express 数据库建立数据库服务器连接。一种解决办法是,改为创建与 SQL Server Express 数据库的空间数据库连接并加以使用。
- 与栅格图层有关的连接和关联不会进行更新。
- SQL 查询不会自动更新。不同的工作空间(例如,个人与文件地理数据库)使用不同的 SQL 语法进行图层定义查询,并且该语法不会自动更新。通过更新图层对象的 definitionQuery 属性,可更正语法。可通过简单的 Python 字符串函数来执行基本的搜索和替换操作。例如,
- 对于个人地理数据库,字段名称用方括号 ([]) 括起来,而对于文件地理数据库,则使用引号 ("")。
-
个人地理数据库的通配符为星号 (*) 和问号 (?),而文件地理数据库的通配符则为百分号 (%) 和下划线 (_)。
-
个人地理数据库中的字符串搜索不区分大小写,但在文件地理数据库中则区分大小写。
-
个人地理数据库使用 UCASE 和 LCASE 转换字符串大小写。文件地理数据库使用 UPPER 和 LOWER。
-
个人地理数据库使用井号 (#) 分隔日期和时间,而在文件地理数据库中它们以单词 date 开头。
- 图层标注表达式也不会自动进行更新。也需要对它们进行更改来支持相应的 SQL 语法。更新图层的 LabelClass SQLQuery 或 expression 属性可更正语法。
- 更新图层的数据源时,地图文档中已保存的选择会被清除(在 ArcMap 中使用图层属性 对话框的设置数据源时也是如此)。目前此问题尚无解决方法,只能手动重新创建地图文档要素选择。
常见情景
基于文件的数据被移至其他文件夹或放到其他网络驱动器中
映射的网络驱动器字母或数据位置的文件夹结构发生了更改,或出现了一些其他情况导致与需要更新的现有数据的连接中断。在这种情况下,仅需将相同数据源重定向到新的文件夹位置或驱动器名称。这种情况几乎适用于包括空间数据库连接文件的所有基于文件的数据结构(例如,shapefile、CAD 数据、个人地理数据库和文件地理数据库以及基于文件的栅格)。
在此情景中,原本直接位于 C:\Project\Data 文件夹下的数据被移到称为 Data2 的子文件夹下。此脚本将更新单个地图文档。
import arcpy mxd = arcpy.mapping.MapDocument(r"C:\Project\Project.mxd") mxd.findAndReplaceWorkspacePaths(r"C:\Project\Data", r"C:\Project\Data2") mxd.saveACopy(r"C:\Project\Project2.mxd") del mxd
将本地路径更改为 UNC 路径
在此情况下,用户希望与可访问其驱动器的其他员工共享地图文档,并且希望员工不必将数据复制到本地驱动器上即可成功打开地图文档。
这种情况涉及到将本地系统路径更改为 UNC 路径。以下脚本将更新文件夹中的所有地图文档。
import arcpy, os folderPath = r"C:\Project" for filename in os.listdir(folderPath): fullpath = os.path.join(folderPath, filename) if os.path.isfile(fullpath): basename, extension = os.path.splitext(fullpath) if extension.lower() == ".mxd": mxd = arcpy.mapping.MapDocument(fullpath) mxd.findAndReplaceWorkspacePaths(r"C:\Project\Data", r"\\ComputerName\Project\Data") mxd.save() del mxd
SDE 连接属性发生更改
出现这种情况的原因有很多 - 可能是服务器名称和/或端口号发生更改;连接包括登录凭据,但出于安全考虑需要将其移除;密码被更改;连接由 3 层连接更改为 2 层直连;或者希望将图层指向不同的版本。无论哪种情况,修改空间数据库连接属性时,只需将路径传递到连接文件即可。
在此情景中,用户希望更改所有地图图层的地理数据库版本。用户创建新的 SDE 连接文件并用新连接文件替换原始连接文件。
import arcpy mxd = arcpy.mapping.MapDocument(r"C:\Project\Project_default.mxd") mxd.findAndReplaceWorkspacePaths(r"C:\Project\Connection to Default.sde", r"C:\Project\Connection to Version1.sde") mxd.saveACopy(r"C:\Project\Project_V1.mxd") del mxd
在此情况下,用户希望移除地图文档中保存的密码信息。地图文档中的数据源来自 SDE 连接文件,在此文件中密码信息随连接信息一起保存。接下来,用户为同一数据库创建了新的 SDE 连接文件,但这次未保存密码信息。在以下脚本中,必须将 validate 参数设置为 False 才能成功移除密码信息。脚本运行完毕后,用户需要登录才能打开生成的地图文档。
import arcpy mxd = arcpy.mapping.MapDocument(r"C:\Project\Project_default.mxd") mxd.findAndReplaceWorkspacePaths(r"C:\Project\Connection with password info saved.sde", r"C:\Project\Connection with no password info saved.sde", False) mxd.saveACopy(r"C:\Project\Project_NP.mxd") del mxd
在不同工作空间类型之间迁移数据
某些通常情况下,用户需要将 shapefile 和个人地理数据库等数据迁移到文件地理数据库或从文件地理数据库迁移到企业级 SDE 连接。由于各地理数据库工作空间存在着细微的差别,因此建议您查看上述“已知局限性”部分以便更好地了解其他可能需要解决的问题。
这种情况涉及在一个地图文档中更新两个不同的工作空间。首先,将 shapefile 重定向到一个称为 Parcels.gdb 的文件地理数据库。然后,将个人地理数据库中的图层重定向到另一个名为 Transportation.gdb 的文件地理数据库。
import arcpy mxd = arcpy.mapping.MapDocument(r"C:\Project\Project.mxd") mxd.replaceWorkspaces(r"C:\Project\Data", "SHAPEFILE_WORKSPACE", r"C:\Project\Data\Parcels.gdb", "FILEGDB_WORKSPACE") mxd.replaceWorkspaces(r"C:\Project\Data\Transportation.mdb", "ACCESS_WORKSPACE", r"C:\Project\Data\Transportation.gdb", "FILEGDB_WORKSPACE") mxd.saveACopy(r"C:\Project\Project2.mxd") del mxd
除了所有数据源被定向到单个文件地理数据库外,此示例与上面的示例完全相同。如果使用值 NONE 代替 old_workspace_type 参数,则可以将多个工作空间类型重定向到单个工作空间类型。
import arcpy mxd = arcpy.mapping.MapDocument(r"C:\Project\Project.mxd") mxd.replaceWorkspaces("", "NONE", r"C:\Project\Data\BackgroundData.gdb","FILEGDB_WORKSPACE") mxd.saveACopy(r"C:\Project\Project2.mxd")
在以下情景中,将个人地理数据库中的所有图层重定向到文件地理数据库。它还可以修复图层的 definitionQuery 属性和标注类的 SQLQuery 属性的 SQL 表达式。例如,个人地理数据库的字段名称用方括号 ([]) 括起来,而文件地理数据库则使用双引号 ("")。通配符也将被替换。
import arcpy mxd = arcpy.mapping.MapDocument(r"C:\Project\Project.mxd") mxd.replaceWorkspaces(r"C:\Project\Data\Parcels.mdb", "ACCESS_WORKSPACE", r"C:\Project\Data\Parcels.gdb", "FILEGDB_WORKSPACE") for lyr in arcpy.mapping.ListLayers(mxd): if lyr.supports("DEFINITIONQUERY"): lyr.definitionQuery = lyr.definitionQuery.replace("[", "\"") lyr.definitionQuery = lyr.definitionQuery.replace("]", "\"") lyr.definitionQuery = lyr.definitionQuery.replace("*", "%") if lyr.supports("LABELCLASSES"): for lblClass in lyr.labelClasses: lblClass.SQLQuery = lblClass.SQLQuery.replace("[", "\"") lblClass.SQLQuery = lblClass.SQLQuery.replace("]", "\"") lblClass.SQLQuery = lblClass.SQLQuery.replace("*", "%") mxd.saveACopy(r"C:\Project\Project2.mxd") del mxd
将单个数据集移到新的文件夹位置
有些情况下,用户只需要移动单个数据集而不是整个工作空间。有时,不用尝试更新地图文档中的所有图层,仅仅集中处理已移动的各项目就可以达到目的。
在该情景中,将名为 MapIndex 的要素类从 Data 文件夹下的文件地理数据库移到 Data2 文件夹下的文件地理数据库副本。
import arcpy mxd = arcpy.mapping.MapDocument(r"C:\Project\Project.mxd") for lyr in arcpy.mapping.ListLayers(mxd): if lyr.supports("DATASOURCE"): if lyr.dataSource == r"C:\Project\Data\Parcels.gdb\MapIndex": lyr.findAndReplaceWorkspacePath(r"Data", r"Data2") mxd.saveACopy(r"C:\Project\Project2.mxd") del mxd
将单个数据集移入和移出地理数据库要素数据集。
地理数据库要素数据集是工作空间的组成部分,而且它们的名称不应包括在工作空间路径中。如果在同一工作空间中将要素类移入或移出地理数据库要素数据集,则不应更新地图文档或图层文件数据源。如果将数据集移到同一类型的其他工作空间,则只需提供新工作空间的路径而无需地理数据库要素数据集名称。如果工作空间类型不同,则对图层调用 findAndReplaceWorkspacePath 将不起作用,您需要改为调用 replaceDataSource。
在此情景中,某个文件地理数据库中的独立要素类会被移动到其他文件地理数据库中的要素数据集。
import arcpy mxd = arcpy.mapping.MapDocument(r"C:\Project\Project.mxd") for lyr in arcpy.mapping.ListLayers(mxd): if lyr.supports("DATASOURCE"): if lyr.dataSource == r"C:\Project\Data\Parcels.gdb\MapIndex": lyr.findAndReplaceWorkspacePath(r"Parcels.gdb", r"Transportation.gdb") mxd.saveACopy(r"C:\Project\Project2.mxd") del mxd
要素类被重命名
方案变更很普遍,有时要素类的名称也会发生更改。这种情况下,指向该数据集的所有图层必然会发生损坏。
在此情景中,将要素类由 MajorRoads 重命名为 Highways,从而导致地图文档或图层文件中的各图层发生了损坏。该脚本将调用 ListBrokenDataSources() 函数查找损坏的数据源,并执行相应的修复操作。
import arcpy mxd = arcpy.mapping.MapDocument(r"C:\Project\Project.mxd") for lyr in arcpy.mapping.ListBrokenDataSources(mxd): if lyr.supports("DATASOURCE"): if lyr.dataSource == r"C:\Project\Data\Transportation.gdb\MajorRoads": lyr.replaceDataSource(r"C:\Project\Data\Transportation.gdb", "FILEGDB_WORKSPACE", "Highways") lyr.name = "Highways" mxd.saveACopy(r"C:\Project\Project2.mxd") del mxd