更新和修复数据源

在许多情况下,您都可能需要修复数据源或重定向数据源至其他位置。ArcGIS Pro 中的目录视图具有更新数据源的功能。然而,在每个相关地图或工程中进行手动更改将会异常麻烦。arcpy.mp 脚本环境提供了多种方法使得您无需打开工程即可自动进行更改。您可针对各个图层或表逐一更新数据源,也可一次更新同一工作空间中的所有图层或表。

以下成员用于更改数据源工作流:

使用 updateConnectionProperties 函数

updateConnectionProperties 函数可以被视为是一个用 current_connection_info 参数替换 new_connection_info 参数的查找与替换函数。这些参数既可以是到工作空间、部分字符串、包含连接属性的字典、定义特定键的部分字典的完整路径,也可以是到数据库连接 (.sde) 文件的路径。

提示:

更新企业级地理数据库图层的数据源时,可以在 current_connection_infonew_connection_info 参数中使用数据库连接文件,如下所示:

aprx.updateConnectionProperties(r'C:\DBConnections\TestGDB.sde', 
                                r'C:\DBConnections\ProductionGDB.sde')

auto_update_joins_and_relates 属性允许您控制是否应更新连接、关联或与图层或表相关的事件。将默认值设为 True。有时,尤其是在更新工程级别的所有数据源时,您不希望这些相关的数据源更新。如果是这种情况,请将此参数设置为 False

在默认情况下,updateConnectionProperties 方法仅在 new_connection_info 是有效数据源的情况下更新数据源。如果将 validate 参数设置为 False,则将数据源设置到该位置,而不考虑其现有位置。这对尚未创建数据时需要更新数据源的情况十分有用。在上述情况下,数据在关联的地图中将显示为已损坏。

要将图层的数据集更改为具有不同名称的要素类,请参阅下方的使用 connectionProperties 字典部分。

以下是一些示例:

  1. 以下脚本将更改工程中所有图层和表的文件地理数据库数据源的完整路径。在本示例中,对文件夹进行了重命名,并将所有矢量数据移动到了这一新位置:

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    aprx.updateConnectionProperties(r'C:\Projects\YosemiteNP\Data\Yosemite.gdb',
                                    r'C:\Projects\YosemiteNP\Vector_Data\Yosemite.gdb')
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")
  2. 以下示例与上述示例非常相似,但该示例将使用部分路径字符串替换数据源。请确保使用部分字符串时,不会在路径中多次发生。您可能不会获得期望的结果。

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    aprx.updateConnectionProperties('Data','Vector_Data')
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")
  3. 以下示例使用地图中所有图层和表的部分路径将个人地理数据库连接替换为文件地理数据库连接:

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    m = aprx.listMaps("Yose*")[0]
    m.updateConnectionProperties('Background.mdb', 'Background_fGDB.gdb')
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")
  4. 以下示例参考地图中的图层,并使用连接属性更新尚未更新新数据源的图层文件中相同图层的连接属性。

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    m = aprx.listMaps('Yose*')[0]
    lyr = m.listLayers('Ranger Stations')[0]
    lyrFile = arcpy.mp.LayerFile(r'C:\Projects\YosemiteNP\LYRXs\Yosemite\OperationalLayers.lyrx')
    
    for l in lyrFile.listLayers():
      if l.name == 'Ranger Stations':
        l.updateConnectionProperties(l.connectionProperties, lyr.connectionProperties)
    
    lyrFile.save()

以下是为企业级地理数据库图层更新数据源的几个示例:

  1. 以下示例将文件地理数据库连接替换为工程中所有图层和表的企业级地理数据库连接 (.sde) 文件的路径。

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    aprx.updateConnectionProperties(r'C:\Projects\YosemiteNP\Vector_Data\Yosemite.gdb',
                                    r'C:\Projects\YosemiteNP\DBConnections\Server.sde')
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")
  2. 以下示例将 current_connection_info 参数中的企业级地理数据库连接文件的连接属性替换为 new_connection_info 参数中的企业级地理数据库连接文件。

    注:

    current_connection_info 参数中指定的企业级地理数据库连接文件无需是用于创建图层的当前连接文件。而情况是,连接文件包含连接属性将用于 updateConnectionProperties 查找和替换功能。

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    aprx.updateConnectionProperties(r'C:\Projects\YosemiteNP\DBConnections\TestGDB.sde',
                                    r'C:\Projects\YosemiteNP\DBConnections\ProductionGDB.sde')
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")
  3. 以下示例将企业级地理数据库连接文件替换为工程中所有图层和表的文件地理数据库的路径。

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    aprx.updateConnectionProperties(r'C:\Projects\YosemiteNP\DBConnections\Server.sde',
                                    r'C:\Projects\YosemiteNP\Local_Data\YosemiteLocal.gdb')
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")

提示:
updateConnectionProperties 函数中将 current_connection_info 替换为 new_connection_info 参数时,如果未找到任何匹配,则您的脚本可能会运行完成,但不会更新任何内容。

使用 connectionProperties 字典

用于更新数据源的 connectionProperties 需要使用连接属性字典。返回的字典取决于其是基于文件的工作空间还是数据库连接,或者取决于图层或表是否具有相关连接或关联。由于存在这种变异性,因此有必要了解连接属性的不同类型以及通过导航字典做出相应更改的方法。例如,具有连接或关联的图层所返回的结果与不具有连接或关联的相同图层返回的结果大为不同。更新连接属性字典的方法是从图层或表中进行参考并检索字典,对其做出必要的更改,然后将修改后的字典重置回您想要使用 updateConnectionProperties 方法更新的图层或表。

显示字典结构的一个不错方法是使用 Python pprint 函数。

import arcpy, pprint
p = arcpy.mp.ArcGISProject('current')
m = p.listMaps()[0]
l = m.listLayers()[0]
pprint.pprint(l.connectionProperties)

例如,不具有连接和关联的基于文件的数据源将如下所示:

{'connection_info': {'database': 'C:\\Projects\\YosemiteNP\\Data\\Yosemite.gdb'}, 
 'dataset': 'RangerStations', 
 'workspace_factory': 'File Geodatabase'}

上述示例是最基本的结构。返回含三个键的字典。connection_info 键值是包含 database 路径的另一个字典。

connectionProperties 字典最常用的案例之一是将图层的数据集更改为具有不同名称的要素类。以下是一些示例:

  1. 以下示例将数据源的数据集名称从 RangerStations 更新为 RangerStationsNew。它还将地理数据库从 Yosemite.gdb 更新为 YosemiteNew.gdb

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    lyr = aprx.listMaps("Main*").listLayers("Ranger Stations")[0]
    find_dict = {'connection_info': {'database': 'C:\\Projects\\YosemiteNP\\Data\\Yosemite.gdb'}, 
                 'dataset': 'RangerStations', 
                 'workspace_factory': 'File Geodatabase'}
    replace_dict = {'connection_info': {'database': 'C:\\Projects\\YosemiteNP\\Data\\YosemiteNew.gdb'}, 
                    'dataset': 'RangerStationsNew', 
                    'workspace_factory': 'File Geodatabase'}
    lyr.updateConnectionProperties(find_dict, replace_dict)
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")
  2. 上面的脚本也可以重新编写为以下内容:

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    lyr = aprx.listMaps("Main*").listLayers("Ranger Stations")[0]
    cp = lyr.connectionProperties
    cp['connection_info']['database'] = 'C:\\Projects\\YosemiteNP\\Data\\YosemiteNew.gdb'
    cp['dataset'] = 'RangerStationsNew'
    lyr.updateConnectionProperties(lyr.connectionProperties, cp)
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")
  3. 也可以在 updateConnectionProperties 方法中使用部分字典,如下面的示例所示。

    以下示例针对工程中的图层将数据源的数据集名称从 PtsInterest 更新为 PointsOfInterest。本示例不会更改要素类所在的地理数据库。相反,它将更新图层以指向同一地理数据库中的另一个要素类:

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    aprx.updateConnectionProperties({'dataset': 'PtsInterest'}, {'dataset': 'PointsOfInterest'})
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")
  4. connectionProperties 字典还可以用于更新基于文件的数据源,例如 shapefile、栅格文件等。以下示例将图层的数据源更改为指向其他文件夹中的新 shapefile。

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    lyr = aprx.listMaps('Main*').listLayers('RoadsShp')[0]
    cp = lyr.connectionProperties
    cp['connection_info']['database'] = 'C:\\Projects\\YosemiteNP\\Data_New'
    cp['dataset'] = 'NewRoads.shp'
    lyr.updateConnectionProperties(lyr.connectionProperties, cp)
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")

使用含企业级地理数据库数据的 connectionProperties 字典

以下是图层的企业级地理数据库数据源 connectionProperties 字典的示例:

{'connection_info': {'authentication_mode': 'OSA',                     
                     'database': 'TestDB',                     
                     'db_connection_properties': 'TestServer',                     
                     'dbclient': 'sqlserver',                     
                     'instance': 'sde:sqlserver:TestServer',                     
                     'password': '*********',                     
                     'server': 'TestServer',                     
                     'user': 'User',                     
                     'version': 'sde.DEFAULT'}, 
'dataset': 'TestDB.USER.RangerStations', 
'workspace_factory': 'SDE'}

已返回三个相同的键作为文件地理数据库图层,但此次 connection_info 值是具有更大数据库连接属性集的字典。这些属性中的任何一个都可以修改。

以下示例将更改工程中所有图层的企业级地理数据库实例和服务器。在此示例中,企业级地理数据库使用操作系统身份验证,并且数据库名称相同。如果用户名和密码相同,则可以在不知道工程中图层凭据的情况下更改实例和服务器,而无需创建新的企业级地理数据库连接文件。

import arcpy
aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
find_dict = {'connection_info': {'db_connection_properties': 'TestServer',
                                 'instance': 'sde:sqlserver:TestServer',
                                 'server': 'TestServer'}}
replace_dict = {'connection_info': {'db_connection_properties': 'ProdServer',
                                    'instance': 'sde:sqlserver:ProdServer',
                                    'server': 'ProdServer'}}
aprx.updateConnectionProperties(find_dict, replace_dict)
aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")

使用具有连接的 connectionProperties 字典

connectionProperties 字典还将显示该图层上存在的所有连接的属性。这些属性中的任何一个都可以修改。

下面的示例显示了具有一个连接的基于文件的数据源 connectionProperties 字典:

{'cardinality': 'one_to_many',
 'destination': {'connection_info': {'database': 'C:\\Projects\\FGDB.gdb'},
                 'dataset': 'tabular_eco',
                 'workspace_factory': 'File Geodatabase'},
 'foreign_key': 'ECO_CODE',
 'join_forward': False,
 'join_type': 'left_outer_join',
 'primary_key': 'CODE',
 'source': {'connection_info': {'database': 'C:\\Projects\\FGDB.gdb'},
            'dataset': 'mex_eco',
            'workspace_factory': 'File Geodatabase'}}

示例显示了具有两个连接的基于文件的数据源 connectionProperties 字典:

{'cardinality': 'one_to_many', 
 'destination': {'connection_info': {'database': 'C:\\Projects\\YosemiteNP\\Data\\BackgroundData.gdb'},                 
                 'dataset': 'census2000',                 
                 'workspace_factory': 'File Geodatabase'}, 
 'foreign_key': 'State_Polygons.State_Name', 
 'join_forward': False, 
 'join_type': 'left_outer_join',
 'primary_key': 'STATE_NAME', 
 'source': {'cardinality': 'one_to_many',
            'destination': {'connection_info': {'database': 'C:\\Projects\\YosemiteNP\\Data\\BackgroundData.gdb'},
                            'dataset': 'census2010',                            
                            'workspace_factory': 'File Geodatabase'},
            'foreign_key': 'State_Name',            
            'join_forward': False,            
            'join_type': 'left_outer_join',            
            'primary_key': 'STATE_NAME',            
            'source': {'connection_info': {'database': 'C:\\Projects\\YosemiteNP\\Data\\BackgroundData.gdb'},
                       'dataset': 'State_Polygons',                       
                       'workspace_factory': 'File Geodatabase'}}}

连接与图层或表相关时,connectionProperties 字典结构将更改。您将不再具有与之前示例中所看到的相同的三个根级别键。要了解其不同的原因,您需要了解保存连接的方法。连接是嵌套的。例如,如果表一和表二都连接到图层,则表一连接到图层,而表二连接到该图层与表一的组合。根级别字典首先描述第二个连接。通过第二个连接的 source 可追踪与原始图层和表 1 的连接。

以下是使用具有连接的 connectionProperties 字典的示例:

  1. 以下示例将修改特定图层的连接的外键。

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\Mexico\MexicoEcology.aprx')
    mexLyr = aprx.listMaps('Layers')[0].listLayers('mex_eco')[0]
    conProps = mexLyr.connectionProperties
    conProps['foreign_key'] = 'ECO_CODE_NEW'
    mexLyr.updateConnectionProperties(mexLyr.connectionProperties, conProps)
    aprx.saveACopy(r"C:\Projects\Mexico\MexicoEcologyNew.aprx")
  2. 部分字典还可以用于 updateConnectionProperties 方法中。以下示例将修改工程中使用指定外键的所有图层的连接属性:

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\Mexico\MexicoEcology.aprx')
    aprx.updateConnectionProperties({'foreign_key': 'ECO_CODE'}, {'foreign_key': 'ECO_CODE_NEW'})
    aprx.saveACopy(r"C:\Projects\Mexico\MexicoEcologyNew.aprx")
  3. 以下示例将针对两个表所连接到的主要图层修改源数据库和数据集,而不更改连接的连接信息:

    import arcpy
    aprx = arcpy.mp.ArcGISProject(r'C:\Projects\YosemiteNP\Yosemite.aprx')
    lyr = aprx.listMaps("Main*").listLayers("State_Polygons")[0]
    conProp = lyr.connectionProperties
    conProp['source']['source']['connection_info']['database'] = 'C:\\Projects\\YosemiteNP\\Vector_Data\\Census.gdb'
    conProp['source']['source']['dataset'] = 'States'
    lyr.updateConnectionProperties(lyr.connectionProperties, conProp)
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")
  4. 以下示例将创建地图中所有图层上所有连接的连接清单。本示例采用 Python 递归函数逻辑来处理不具有连接或具有任意连接数的图层:

    import arcpy
    
    def ListJoinsConProp(cp, join_count=0):
        if 'source' in cp:
            if 'destination' in cp:
                print(' '*6, 'Join Properties:')
                print(' '*9, cp['destination']['connection_info'])
                print(' '*9, cp['destination']['dataset'])
                join_count += 1
                return ListJoinsConProp(cp['source'], join_count)
        else:
            if join_count == 0:
                print(' '*6, '- no join')
    
    aprx = arcpy.mp.ArcGISProject(r"C:\Projects\Mexico\MexicoEcology.aprx")
    m = aprx.listMaps()[0]
    for lyr in m.listLayers():
        print(f"LAYER: {lyr.name}")
        if lyr.supports("dataSource"):
            cp = lyr.connectionProperties
            if cp is not None:
                ListJoinsConProp(cp)
  5. 以下示例将显示地图中图层的连接属性。与上面的示例类似,该示例还采用 Python 递归函数逻辑来处理不具有连接或具有任意连接数的图层:

    import arcpy
    
    def ConPropsWithJoins(cp):
        if 'source' in cp:
            return ConPropsWithJoins(cp['source'])
        else:
            print(' '*6, 'database:', cp['connection_info']['database'])
            print(' '*6, 'dataset:', cp['dataset'])
            print(' '*6, 'workspace_factory:', cp['workspace_factory'])
    
    aprx = arcpy.mp.ArcGISProject(r"C:\Projects\Mexico\MexicoEcology.aprx")
    m = aprx.listMaps()[0]
    for lyr in m.listLayers():
        print(f"LAYER: {lyr.name}")
        if lyr.supports("dataSource"):
            cp = lyr.connectionProperties
            if cp is not None:
                ConPropsWithJoins(cp)

通过 CIM 更新数据源

ArcGIS Pro 2.4 开始,Python 开发人员可以对制图信息模型 (CIM) 进行细化访问,并可以访问工程或文档中永久保留的更多设置、属性和功能。这对于更新数据源工作流可能很有用。有关详细信息,请参阅以下内容:

如果特定的数据源工作流难以使用 updateConnectionProperties 函数完成,则修改图层的 CIM 结构是一种选择。Python CIM 访问 主题描述了 CIM 对象模型的 JSON 结构。了解此结构将使您能够更新图层的 CIM。

例如,以下是 CAD 图层数据源的 JSON 表示。以下 JSON 不是图层的完整 CIM 结构。而是一个仅显示 dataConnection 节点的片段。

"dataConnection" : {
  "type" : "CIMFeatureDatasetDataConnection",
  "featureDataset" : "parcels.dwg",
  "workspaceConnectionString" : "DATABASE=C:\\Projects\YosemiteNP\\CAD",
  "workspaceFactory" : "Cad",
  "dataset" : "Polyline",
  "datasetType" : "esriDTFeatureClass"
}

以下是一些使用 CIM 更新数据源的示例:

  1. 以下示例引用了地图中的 CAD 图层。然后,将更新图层以指向新的 CAD 文件。该脚本假定新的 CAD 文件与先前的 CAD 文件位于同一文件夹中。

    注:

    为 CAD 图层更新数据源以指向新的 CAD 文件需要修改 CIM。但是,仅更改 CAD 文件所在的文件夹可以使用 updateConnectionProperties 函数完成。

    import arcpy
    
    aprx = arcpy.mp.ArcGISProject(r"C:\Projects\YosemiteNP\Yosemite.aprx")
    m = aprx.listMaps('CAD')[0]
    # Select the CAD sub layer to update
    lyr = m.listLayers('Parcels')[0]
    
    # Access layer CIM
    lyrCIM = lyr.getDefinition("V2")
    dc = lyrCIM.featureTable.dataConnection
    
    # Update the feature dataset with the new CAD file name 
    dc.featureDataset = "NewParcels.dwg"
    
    # Update layer CIM
    lyr.setDefinition(lyrCIM)
    
    aprx.saveACopy(r"C:\Projects\YosemiteNP\YosemiteNew.aprx")
  2. 该脚本将更改图层上的关联的数据源。该脚本将更改关联的数据集、要素数据集和文件地理数据库。

    import arcpy
    
    # Specify the new relate properties
    newGDB = "FGDB2.gdb"
    newFeatureClass = "Cities2"
    newFeatureDataSet = "USA2"
    newRelateName = "New Relate"
    
    # Reference project, map and layer 
    p = arcpy.mp.ArcGISProject('current')
    m = p.listMaps('Relate Map')[0]
    l = m.listLayers('States')[0]
    
    # Get the layer's CIM definition
    lyrCIM = l.getDefinition('V2')         
    
    # Get the first relate on the layer
    relate = lyrCIM.featureTable.relates[0]
    
    # Get the data connection properties for the relate
    dc = relate.dataConnection
    
    # Change the connection string to point to the new File Geodatabase
    dc.workspaceConnectionString = dc.workspaceConnectionString.replace("FGDB.gdb", newGDB)
    
    # Change the dataset name
    dc.dataset = newFeatureClass
    
    # If the data is in a Feature Dataset, then update it 
    if hasattr(dc, "featureDataset"):
        dc.featureDataset = newFeatureDataSet
        
    # Change the relate's name
    relate.name = newRelateName
    
    # Set the layer's CIM definition
    l.setDefinition(lyrCIM)