findAndReplaceWorkspacePaths crashes when mxd contains image layers

3863
17
02-12-2011 03:47 AM
DonMcClimans
New Contributor
I am moving a large number of mxd files, and need to update the data sources for various layer files. I have written a script that uses findAndReplaceWorkspacePaths. The script works fine as long as the mxd does not contain an image service layer. If it does contain such a layer, the script crashes. I am not trying to change the path in the image layer, but other layers in that mxd file.

This looks like bug NIM060315 http://resources.arcgis.com/content/nimbus-bug?bugID=TklNMDYwMzE1, except I am not trying to change the image layer itself.

The bug report lists no workarounds. Is there anything I can do? Doing these by hand will be a big undertaking (and error prone).

Thanks
0 Kudos
17 Replies
deleted-user-9ppwtkjpkhrH
New Contributor II
I'm at SP 2.  The following Python script was a successfull work around.  Note, I'm not experienced in arcpy so judge accordingly (although, hey, it does work!):

import os, sys, arcpy
import arcpy.mapping as MAP

mxdPath = sys.argv[1]
oldDataSource = r'P:\cby\gisprojects\umpqua\Phase_II'
newDataSource = r'P:\cby\gisprojects\umpqua\Soup_Cr_VRH'

arcpy.overwriteoutput = 1
mxd = MAP.MapDocument(mxdPath)

for lyr in MAP.ListLayers(mxd):
    if lyr.supports("DATASOURCE"):  # note, this caught some of the image service layers but let one slip through
        if lyr.supports("SERVICEPROPERTIES"):
            if lyr.serviceProperties['ServiceType'] == "ImageServer":  # note, this caught the image service layer that slipped through the first "if" statement
                pass
            else:
                lyr.findAndReplaceWorkspacePath(oldDataSource, newDataSource, False)
        else:
            lyr.findAndReplaceWorkspacePath(oldDataSource, newDataSource, False)
mxd.save()

del mxdPath
del oldDataSource
del newDataSource
del mxd
0 Kudos
JeffBarrette
Esri Regular Contributor
This should have been addressed at 10.0 SP3 with NIM067149: Block Web service layers from supporting layer.dataSource.  The example in the previous post solves one problem but will introduce another if something.

The correct solution should be:


I just confirmed this on a 10.0 SP3 machine. Image Server layers no longer show up as layers that support the lyr.support("DATASOURCE") property.

Before attempting to change a data source, check to see if this property is true.

for lyr in arcpy.mapping.ListLayers(mxd):
    if lyr.supports("DATASOURCE"): If you are still having a problem, please send me a map package so I can test with your data.



Thanks,
Jeff
0 Kudos
StephenIrving
New Contributor II
Hello Jeff,

I, too, am having a great deal of trouble with this issue.  I am running ArcGIS 10 SP4 and attempting the following:
import arcpy, os
folderPath = r"G:\US_Army_COE_Sac\ERS\Ft_Irwin\_MXDs\Checker"
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":
            print fullpath + "\n...Checking"
            mxd = arcpy.mapping.MapDocument(fullpath)
            for lyr in arcpy.mapping.ListLayers(mxd):
                if lyr.supports("DATASOURCE"):
                    mxd.findAndReplaceWorkspacePaths(r"G:\US_Army_COE_Sac", r"G:\US_Army_COE_Sac\ERS\Ft_Irwin")
            print fullpath + "...Updated"
            mxd.save()
del mxd

I have also tried looping through each layer after the "DATASOURCE" test using layer.findAndReplaceWorkspacePath() with the same result of a full crash. The MXDs each have a Bing streaming imagery layer in the TOC and the script works fine if i remove it.

Thanks in advance for any ideas on why this is still not working....

Steve
0 Kudos
JeffBarrette
Esri Regular Contributor
I was able to reproduce using Bing Maps Aerial basemap.  The higher level "Basemap" group layer does NOT support "DATASOURCE" but the sub-layer "BingMapsAerial" unfortunately does (the value is an empty string).  We'll need to correct this.

In the meantime can you try:

for lyr in arcpy.mapping.ListLayers(mxd):
    if lyr.supports("DATASOURCE") and lyr.supports("DATASETNAME"):



The BingMapsAerial sub-layer does NOT support "DATASETNAME".
0 Kudos
StephenIrving
New Contributor II
Sweet!

Thank you that works well!

Steve
0 Kudos
StephenIrving
New Contributor II
Sweet!

Thank you that works well!

Steve


Looks like i spoke a bit too soon.  Now it is throwing this error:

Executing: MXDSourceUpdate G:\US_Army_COE_Sac\ERS\Ft_Irwin\_MXDs\_Reports\Workplan G:\US_Army_COE_Sac\ERS G:\US_Army_COE_Sac\ERS\Ft_Irwin
Start Time: Tue May 15 17:00:57 2012
Running script MXDSourceUpdate...
Traceback (most recent call last):
  File "D:\TEMP\_Python\mxd_source_update_20120515.py", line 33, in <module>
    lyr.findAndReplaceWorkspacePath(origSource2, newSource2,"FALSE")
  File "C:\Program Files\ArcGIS\Desktop10.0\arcpy\arcpy\utils.py", line 181, in fn_
    return fn(*args, **kw)
  File "C:\Program Files\ArcGIS\Desktop10.0\arcpy\arcpy\_mapping.py", line 601, in findAndReplaceWorkspacePath
    return convertArcObjectToPythonObject(self._arc_object.findAndReplaceWorkspacePath(*gp_fixargs((find_workspace_path, replace_workspace_path, validate), True)))
ValueError: Layer: Unexpected error
Layer: Unexpected error
Completed script MXDSourceUpdate...
Failed to execute (MXDSourceUpdate).
Failed at Tue May 15 17:01:00 2012 (Elapsed Time: 3.00 seconds)

I am not sure, but is seems like the arcpy module is trying to validate the paths.  My understanding was the script would work regardless of whether or not it found a valid path.  I tried explicitly setting the validation to "FALSE" which yielded the same result.  My MXDs typically have layers referencing many sources, I was hoping this tool would function as a true find and replace.  That is, it would ignore any layers in which it did not "find" the search string.

Any ideas?

Here is my code:

import arcpy, os

try:

    #Read input parameters from GP dialog
    folderPath = arcpy.GetParameterAsText(0)
    origSource = arcpy.GetParameterAsText(1)
    newSource = arcpy.GetParameterAsText(2)

    #Loop through each MXD file
    origSource2 = origSource.replace("\\","/")
    newSource2 = newSource.replace("\\","/")
    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)
                print fullpath + "\n\n...Checking"
                for lyr in arcpy.mapping.ListLayers(mxd):
                    if lyr.supports("DATASOURCE") and lyr.supports("DATASETNAME"):
                        lyr.findAndReplaceWorkspacePath(origSource2, newSource2,"FALSE")
                print fullpath + "\n\n...Updated"
                mxd.save()
    del mxd


    #Delete variables that reference data on disk
    del folderPath, origSource, newSource, filename, fullpath

except Exception, e:
        import traceback
        map(arcpy.AddError, traceback.format_exc().split("\n"))
        arcpy.AddError(str(e))

BTW it actually still crashes completely if I try to use mxd.findAndReplaceWorkspacePaths() after the layer test.  I would prefer this method as it is supposed to update all layers including table layers which are apparently missed by the layer.findAndReplaceWorkspacePath()... but maybe i am just missing something?
0 Kudos
JeffBarrette
Esri Regular Contributor
I'm not sure this logic will work.  For example, let us say I have a layer with the following data source:

C:\\temp\\lines

Your code [origSource2 = origSource.replace("\\","/")] is converting the string to:

C:/temp/lines

Then you are trying to perform a find and replace with a "find" string that does NOT exist in any of your sources because you changed it but perhaps that is why you are using validate=False.  I don't know enough to assess the error.

Can you send me just your MXD (no data) to jbarrette@esri.com.  From that I should be able to determine the data sources.

Jeff
0 Kudos
StephenIrving
New Contributor II
"I may have found the issue.  It helps to try real scripts against real MXDs.

Change this line:
lyr.findAndReplaceWorkspacePath(r"G:\US_Army_COE_Sac\ERS\Ft_Irwin", r"G:\US_Army_COE_Sac","FALSE")

To:
lyr.findAndReplaceWorkspacePath(r"G:\US_Army_COE_Sac\ERS\Ft_Irwin", r"G:\US_Army_COE_Sac", False)

The validation parameter is a Python Boolean, not a string.  You want to use False with an upper case "F", lower case "alse")

Jeff"


The above worked perfectly! Thanks again!
0 Kudos