Select to view content in your preferred language

Update\change features data source.

4245
19
01-28-2014 01:10 PM
TonyAlmeida
MVP Regular Contributor
I have some layers that i need to change the data source for.

The current script i have gives me the following error. I don't know why.
if anyone would be kind to help me out please?
thanks.


import arcpy
from arcpy import env
from arcpy import mapping

workspace = arcpy.env.workspace =  "C:/GIS/MAPBOOK/Proposed Zoning Book"

arcpy.env.overwriteOutput = True

mxdList = arcpy.ListFiles("*.mxd")

for mxd in mxdList:
    mxd = workspace + "//" + mxd
    for lyr in arcpy.mapping.ListLayers(mxd): 
        if lyr.name == "PROPOSED ZONING":
                lyr.replaceDataSource("C:/Users/t***a/AppData/Roaming/ESRI/Desktop10.1/ArcCatalog/DSD15_SQLEXPRESS.gds/DSD/DSD.DBO.MUNICIPALITY", "SHAPEFILE_WORKSPACE", "DSD.DBO.FUTURE_LAND_USE_ZONING")
    for lyr in arcpy.mapping.ListLayers(mxd): 
        if lyr.name == "CITY LIMITS":
                lyr.replaceDataSource("C:/Users/t***a/AppData/Roaming/ESRI/Desktop10.1/ArcCatalog/DSD15_SQLEXPRESS.gds/TonyOneWay/TonyOneWay.DBO.Canyon_Features", "SHAPEFILE_WORKSPACE", "TonyOneWay.DBO.City_Limits")            
    print "Successfully updated data sources"
    mxd.save()


Error
Traceback (most recent call last):
  File "C:\GIS\Python Scripts\Change Data Source MXD 2.py", line 13, in <module>
    for lyr in arcpy.mapping.ListLayers(mxd):
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\utils.py", line 181, in fn_
    return fn(*args, **kw)
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\mapping.py", line 1500, in ListLayers
    result = mixins.MapDocumentMixin(map_document_or_layer).listLayers(wildcard, data_frame)
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\arcobjects\mixins.py", line 823, in listLayers
    layers = self.layers
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\arcobjects\mixins.py", line 683, in layers
    for frame in reversed(self.dataFrames):
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\arcobjects\mixins.py", line 695, in dataFrames
    return map(convertArcObjectToPythonObject, self.pageLayout.dataFrames)
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\arcobjects\mixins.py", line 679, in pageLayout
    return convertArcObjectToPythonObject(self._mxd._arc_object.pageLayout)
AttributeError: 'unicode' object has no attribute '_arc_object'
Tags (2)
0 Kudos
19 Replies
MichaelVolz
Esteemed Contributor
Try changing mxd = workspace + "//" + mxd

to

mxd = workspace + "/" + mxd

Not sure if this will work either as that might be an escape character.

Maybe try mxd = workspace + r"/" + mxd
0 Kudos
JamesCrandall
MVP Alum
This works to get you past the problem of referencing the layers in each of the .mxd's (I am just printing them lyr name, but you could continue to implement your desired process from that point on):

import os
import arcpy

path = r'C:\GIS\MAPBOOK\Proposed Zoning Book'
for fileName in os.listdir(path):
   fullPath = os.path.join(path, fileName)
   if os.path.isfile(fullPath):
      basename, extension = os.path.splitext(fullPath)
      if extension == ".mxd":
         mxd = arcpy.mapping.MapDocument(fullPath)
         for lyr in arcpy.mapping.ListLayers(mxd):
            print lyr



edit: you  need to add the import os

"fullpath" references corrected to "fullPath" throughout.  sorry (case is important!).
0 Kudos
TonyAlmeida
MVP Regular Contributor
I started a new window and retyped the script and i got past the AttributeError: 'unicode' object has no attribute '_arc_object' error.

but none of the workspace paths were updated.

it error out on me on line 18.

C:\GIS\MAPBOOK\Proposed Zoning Book\Proposed_ZoningMapBook_Page_1.mxd is being processed

Traceback (most recent call last):
  File "C:\GIS\Python Scripts\Change Data Source MXD 2.py", line 18, in <module>
    lyr.replaceDataSource(r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\DSD\DSD.DBO.MUNICIPALITY", "SDE_WORKSPACE", "DSD.DBO.FUTURE_LAND_USE_ZONING")
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\utils.py", line 181, in fn_
    return fn(*args, **kw)
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\_mapping.py", line 680, in replaceDataSource
    return convertArcObjectToPythonObject(self._arc_object.replaceDataSource(*gp_fixargs((workspace_path, workspace_type, dataset_name, validate), True)))
ValueError: Layer: Unexpected error



CODE
import arcpy
from arcpy import env
from arcpy import mapping

workspace = arcpy.env.workspace =  "C:\GIS\MAPBOOK\Proposed Zoning Book"

arcpy.env.overwriteOutput = True

mxdList = arcpy.ListFiles("*.mxd")

for mxd in mxdList:
    mxd = workspace + "\\" + mxd
    print mxd + " is being processed"
    mxd = arcpy.mapping.MapDocument(mxd)
    mxd.findAndReplaceWorkspacePaths(r"C:\Users\t**a\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DBO.DSD.VECTOR.sde",r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\DSD")
    for lyr in arcpy.mapping.ListLayers(mxd): 
        if lyr.name == "PROPOSED ZONING":
                lyr.replaceDataSource(r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\DSD\DSD.DBO.MUNICIPALITY", "SDE_WORKSPACE", "DSD.DBO.FUTURE_LAND_USE_ZONING")
    for lyr in arcpy.mapping.ListLayers(mxd): 
        if lyr.name == "CITY LIMITS":
                lyr.replaceDataSource(r"C:\Users\t**a\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\TonyOneWay\TonyOneWay.DBO.Canyon_Features", "FILEGDB_WORKSPACE", "TonyOneWay.DBO.City_Limits")            
    print "Successfully updated data sources"
    mxd.save

0 Kudos
JamesCrandall
MVP Alum
I started a new window and retyped the script and i got past the AttributeError: 'unicode' object has no attribute '_arc_object' error.

but none of the workspace paths were updated.

it error out on me on line 18.



That is because you are passing "mxd" as a string.  It needs to be a geoproc object.  This will work:


path = r'C:\GIS\MAPBOOK\Proposed Zoning Book'
for fileName in os.listdir(path):
   fullPath = os.path.join(path, fileName)
   if os.path.isfile(fullPath):
      basename, extension = os.path.splitext(fullPath)
      if extension == ".mxd":
         mxd = arcpy.mapping.MapDocument(fullPath)
         print fullPath
         print mxd
         for lyr in arcpy.mapping.ListLayers(mxd):
            if lyr.name == "PROPOSED ZONING":
                lyr.replaceDataSource(r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\DSD\DSD.DBO.MUNICIPALITY", "SDE_WORKSPACE", "DSD.DBO.FUTURE_LAND_USE_ZONING")
            elif lyr.name == "CITY LIMITS":
                lyr.replaceDataSource(r"C:\Users\t**a\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\TonyOneWay\TonyOneWay.DBO.Canyon_Features", "FILEGDB_WORKSPACE", "TonyOneWay.DBO.City_Limits")            
         print "Successfully updated data sources"
         mxd.save

0 Kudos
MichaelVolz
Esteemed Contributor
Tony:

I would try to avoid using mxd as a variable that is referencing multiple objects.
0 Kudos
TonyAlmeida
MVP Regular Contributor
I tried the following and got on line 21,
Proposed Zoning layer data source  in the 1st mxd did not update.

C:\GIS\MAPBOOK\Proposed Zoning Book\Proposed_ZoningMapBook_Page_1.mxd
<geoprocessing Map object object at 0x02965400>

Traceback (most recent call last):
  File "C:\GIS\Python Scripts\Change Data Source MXD 2.py", line 21, in <module>
    lyr.replaceDataSource(r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\TonyOneWay\TonyOneWay.DBO.Canyon_Features", "FILEGDB_WORKSPACE", "TonyOneWay.DBO.City_Limits")
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\utils.py", line 181, in fn_
    return fn(*args, **kw)
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\_mapping.py", line 680, in replaceDataSource
    return convertArcObjectToPythonObject(self._arc_object.replaceDataSource(*gp_fixargs((workspace_path, workspace_type, dataset_name, validate), True)))
ValueError: Layer: Unexpected error



Code
import arcpy, os
from arcpy import env
from arcpy import mapping


arcpy.env.overwriteOutput = True

path = r'C:\GIS\MAPBOOK\Proposed Zoning Book'
for fileName in os.listdir(path):
   fullPath = os.path.join(path, fileName)
   if os.path.isfile(fullPath):
      basename, extension = os.path.splitext(fullPath)
      if extension == ".mxd":
         mxd = arcpy.mapping.MapDocument(fullPath)
         print fullPath
         print mxd
         for lyr in arcpy.mapping.ListLayers(mxd):
            if lyr.name == "PROPOSED ZONING":
                lyr.replaceDataSource(r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\DSD\DSD.DBO.MUNICIPALITY", "FILEGDB_WORKSPACE", "DSD.DBO.FUTURE_LAND_USE_ZONING")
            elif lyr.name == "CITY LIMITS":
                lyr.replaceDataSource(r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\TonyOneWay\TonyOneWay.DBO.Canyon_Features", "FILEGDB_WORKSPACE", "TonyOneWay.DBO.City_Limits")            
         print "Successfully updated data sources"
         mxd.save


0 Kudos
JohnDye
Deactivated User
A helpful hint someone gave me when I first starting coding was to write nothing but comments before ever writing a single line of code. It accomplishes a few things. First, it ensures your code is always well documented. Secondly, it allows you to easily determine the intent of a line of code without having to decipher all of the variables and arguments, logic and flow in your code in order to understand the intent. Third, it allows you to quickly see how you can improve your code down the road. As you get better, you'll realize more efficient ways of doing things and you'll be able to easily see that in your code if you have a comment telling you the plain text intent and a line of code showing how you did it in the past.

Print statements are always really helpful too because you can watch your code process in the Python window and see where things get hung up, take too long or perhaps flow in a manner you didn't expect.

I use both of those methods constantly it's made life much easier..

I took a closer look at your code and found that your lyr.replaceDataSource functions appeared to be indented too far. I also commented up some places where changes could be made to make things more efficient. A Syntax Check in your IDE should catch that.
One odd thing I did notice is that the path you're providing in your replaceDataSource function looks like an SDE Connection. If that's the case, you need to change the parameter from Shapefile Workspace to SDE.

Give it a shot and let us know how it goes.

import arcpy
from arcpy import env

# Create a variable called Workspace to hold the workspace path
# Doing it this way allows us to continually reference the 'workspace' variable
# whenever we want to get to that path. Also, use the raw string literal format to
# reference the path. The way you had it before is ok, but using forward slashes can
# cause issues with inconsistencies in future path definitions, especially when joining
# or concatenating paths.
workspace = r"C:\GIS\MAPBOOK\Proposed Zoning Book"
print "Workspace = " + str(workspace)

# Now set the Workspace by referencing the path contained in the 'workspace' variable
arcpy.env.workspace = workspace
print "Environment Workspace set to: " + str(workspace)

# Set the overwriteOutput environment parameter to True
arcpy.env.overwriteOutput = True
print "Overwrite Outputs: True"

# Create a list of MXDs in the workspace and populate the results in the variable
# 'mxdList'
mxdList = arcpy.ListFiles("*.mxd")

# Validate that results were actuall populated in the 'mxdList' variable and that it is
# not empty
# If the number of items (len) contained in the mxdList is equal to 0:
if len(mxdList) == 0:
    # Raise an error because there are no MXDs in the workspace!!
    raise Exception("No MXDs were found in the workspace!")
# Otherwise:
else:
    # Do nothing and continue
    pass

# Inform how many MXDs were returned from the Workspace and populated in the 'mxdList'
print "Found " + str(len(mxdList) + " MXDs in the Workspace")

# For each mxd populated in the mxdList:
for mxd in mxdList:
    # Change the variable name to 'targetMXD' because you already have an 'mxd'
    # variable instantied as the current mxd being executed against in the loop.
    # This could cause problems with execution and at the least, can be confusing
    targetMXD = workspace + "//" + mxd
    # For each layer in the 'targetMXD'
    for lyr in arcpy.mapping.ListLayers(targetMXD)[0]:
        # If the given 'lyr' is "PROPOSED ZONING"
        if lyr.name == "PROPOSED ZONING":
            print "Found Layer with name: " + str(lyr.name) + ". Replacing Datasource..."
            # Replace the  Layer's datasource with the below
            lyr.replaceDataSource("C:/Users/t***a/AppData/Roaming/ESRI/Desktop10.1/ArcCatalog/DSD15_SQLEXPRESS.gds/DSD/DSD.DBO.MUNICIPALITY", "SHAPEFILE_WORKSPACE", "DSD.DBO.FUTURE_LAND_USE_ZONING")
            print "Success"
        # Otherwise if the given 'lyr.name' is "CITY LIMITS"
        elif lyr.name == "CITY LIMITS":
            print "Found Layer with name: " + str(lyr.name) + ". Replacing Datasource..."
            # replace the given Layer's Datasource with the below
            lyr.replaceDataSource("C:/Users/t***a/AppData/Roaming/ESRI/Desktop10.1/ArcCatalog/DSD15_SQLEXPRESS.gds/TonyOneWay/TonyOneWay.DBO.Canyon_Features", "SHAPEFILE_WORKSPACE", "TonyOneWay.DBO.City_Limits")
            print "Success"
    print "Successfully updated data sources"
    mxd.save()


Update: Wow. I stopped to take a call and this post progressed quick!!!
0 Kudos
JamesCrandall
MVP Alum
I tried the following and got on line 21,
Proposed Zoning layer data source  in the 1st mxd did not update.

C:\GIS\MAPBOOK\Proposed Zoning Book\Proposed_ZoningMapBook_Page_1.mxd
<geoprocessing Map object object at 0x02965400>

Traceback (most recent call last):
  File "C:\GIS\Python Scripts\Change Data Source MXD 2.py", line 21, in <module>
    lyr.replaceDataSource(r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\TonyOneWay\TonyOneWay.DBO.Canyon_Features", "FILEGDB_WORKSPACE", "TonyOneWay.DBO.City_Limits")
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\utils.py", line 181, in fn_
    return fn(*args, **kw)
  File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\_mapping.py", line 680, in replaceDataSource
    return convertArcObjectToPythonObject(self._arc_object.replaceDataSource(*gp_fixargs((workspace_path, workspace_type, dataset_name, validate), True)))
ValueError: Layer: Unexpected error



Code
import arcpy, os
from arcpy import env
from arcpy import mapping


arcpy.env.overwriteOutput = True

path = r'C:\GIS\MAPBOOK\Proposed Zoning Book'
for fileName in os.listdir(path):
   fullPath = os.path.join(path, fileName)
   if os.path.isfile(fullPath):
      basename, extension = os.path.splitext(fullPath)
      if extension == ".mxd":
         mxd = arcpy.mapping.MapDocument(fullPath)
         print fullPath
         print mxd
         for lyr in arcpy.mapping.ListLayers(mxd):
            if lyr.name == "PROPOSED ZONING":
                lyr.replaceDataSource(r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\DSD\DSD.DBO.MUNICIPALITY", "FILEGDB_WORKSPACE", "DSD.DBO.FUTURE_LAND_USE_ZONING")
            elif lyr.name == "CITY LIMITS":
                lyr.replaceDataSource(r"C:\Users\talmeida\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\TonyOneWay\TonyOneWay.DBO.Canyon_Features", "FILEGDB_WORKSPACE", "TonyOneWay.DBO.City_Limits")            
         print "Successfully updated data sources"
         mxd.save





So what I have provided does actually get you past your initial error/problem and now you have come across a new one: your layer reference is invalid. 

".DBO" sounds more like something from a SDE/Database schema, not a file geodatabase or something on disk in a folder.  SQLEXPRESS.gds?  I don't think I've seen that one before.

You can access SDE data by pointing to the .sde file http://resources.arcgis.com/en/help/main/10.1/index.html#/Layer/00s300000008000000/
0 Kudos
TonyAlmeida
MVP Regular Contributor
Yes i did get past my initial problem. I am a newb to python. and thank you for the advice on comments.

the NEW location for Proposed Zoning and City Limits is in a SQL Server Express database.

The code you provided gave me an error on line 37

Workspace = C:\GIS\MAPBOOK\Proposed Zoning Book
Environment Workspace set to: C:\GIS\MAPBOOK\Proposed Zoning Book
Overwrite Outputs: True

Traceback (most recent call last):
  File "C:/GIS/Python Scripts/Change Data Source MXD_test.py", line 37, in <module>
    print "Found " + str(len(mxdList) + " MXDs in the Workspace")
TypeError: unsupported operand type(s) for +: 'int' and 'str'
0 Kudos
TonyAlmeida
MVP Regular Contributor
my MXD's are located here C:\GIS\MAPBOOK\Proposed Zoning Book

The layer Proposed Zoning was located here (O:\GIS\COUNTY MAPS\Proposed Zoning\FutureLandUseZoning_8_4_2011.shp)

The layer Proposed Zoning was moved to a SQL SQL Server Express instance database, located (r"C:\Users\t**a\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\DSD\DSD.DBO.MUNICIPALITY")

There is also other layers that i need the data source update like IMPACT AREA, FLOODWAY, Zoning, RAILROADS etc that are on an SDE SQL server.

Layer IMPACT AREA old location (C:\Users\t**a\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DBO.DSD.VECTOR.sde\dsd."CC\TALMEIDA".DEVELOPEMENT\dsd."CC\TALMEIDA".CURRENT_IMPACT_AREA)

New IMPACT AREA  location(C:\Users\t**a\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\DSD\DSD.DBO.DEVELOPEMENT\DSD.DBO.CURRENT_IMPACT_AREA

Layer FLOODWAY old location (C:\Users\t**a\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DBO.DSD.VECTOR.sde\dsd."CC\TALMEIDA".FEMA09\dsd."CC\TALMEIDA".FEMA09_FLOODWAY)

New FLOODWAY  location (C:\Users\t**a\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\DSD\DSD.DBO.FEMA09\DSD.DBO.FEMA09_FLOODWAY)

Layer Zoning old  location (C:\Users\t**a\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DBO.DSD.VECTOR.sde\dsd."CC\TALMEIDA".MUNICIPALITY\dsd."CC\TALMEIDA".CURRENT_ZONING)

New Zoning location(C:\Users\t**a\AppData\Roaming\ESRI\Desktop10.1\ArcCatalog\DSD15_SQLEXPRESS.gds\DSD\DSD.DBO.MUNICIPALITY\DSD.DBO.CURRENT_ZONING)
0 Kudos