Python script for mass find and replace of workspace path?

12592
24
01-23-2013 11:20 AM
KevinBladon
New Contributor II
Hi folks,

As our research group has run out of space on a network drive, our IT department just moved all of our folders/data to a new network drive.  However, in doing this all of the folder structures for the data locations have changed.  As a result, none of our project files (e.g., .mxd) are functioning.  As we have ~150-200 .mxd project files does anyone know if there is a piece of code which can automatically work through all of the ArcGIS related folders and find and replace the mapped network drive info or the folder structure.

I found this piece of code, but this only works for a single file.  What I would like to be able to do is a batch process to look through all folders/subfolders/files and find and replace the workspace path (Note: we are working with ArcGIS 10.1).

import arcpy
mxd = arcpy.mapping.MapDocument(r"C:\Project\Project.mxd")
mxd.findAndReplaceWorkspacePaths(r"C:\Project\Data", r"D:\Project\Data")
mxd.saveACopy(r"D:\Project\Project.mxd")
del mxd

Thanks.
Tags (2)
24 Replies
JaredPilbeam1
Occasional Contributor

Hi Ian,

Thanks. Basically, all my map documents have a broken link now for that picture element. Since the directory was changed,  \\Jackson\gis_dept\Data\Images\County-logo-1x1Small.JPG" is a dead path. As you said, I could update the path manually, but it's too time consuming. 

If and when I find a method to fix broken picture links and then repair them I'll post it.

0 Kudos
IanMurray
Frequent Contributor

Is the path for all the broken pictures the one you noted above?  It would be fairly easy to have a script walk through all the map documents in a directory and replace all the picture elements with that specific path with a new path.

Also I noted something fairly interesting, when a link is working to an picture element, the element name is just the name of the image itself without the file extension, whereas when I opened your map with broken links, the name was now the filepath to where the image was, instead of just the file name.

0 Kudos
IanMurray
Frequent Contributor

The reason your script wasn't changing anything was because as I noted above the image name was no longer just the file name of the picture, but the full path to the picture.  I used the python window in the map document and managed to change your picture element to a site plan jpg on my desktop.  Your code would have worked fine, but since the elm.name wasn't what you thought it was, it actually didn't change anything.

If that is the particular path you need to replace on all your mxds, here is some starter script to get you going on editting them all.  This should walk through all the .mxd files in a workspace and any .mxds in any folders within that workspace, check for picture elements with the broken file name and fix to your new image.  I would take 2 or 3 map documents into a separate folder to test this.  Only thing you need to do is change the workspace to the folder where the map documents are.

import arcpy
from arcpy import env

#Change to Directory with you map documents, 
env.workspace = r"YourDirectoryHere"

#Recursively walking workspace
for root, dirs, files in os.walk(env.workspace):
     for f in files:
          if f.endswith(".mxd"):
               mxd = arcpy.mapping.MapDocument(os.path.join(root , f))
               print "Current map being checked is " + mxd.filepath
                    for elem in arcpy.mapping.ListLayoutElements(mxd, "PICTURE_ELEMENT", "*logo")):
                         if elem.name == r"\\Jackson\gis_dept\Data\Images\County-logo-1x1Small.JPG":
                              elem.sourceImage = r"P:\Images\County-logo.JPG"
                    del elem
               del mxd
0 Kudos
JaredPilbeam1
Occasional Contributor

That looks great. But, it's giving me an error. Also, i'm curious why your line 13 is indented? That was giving me an error before i dedented. 

0 Kudos
IanMurray
Frequent Contributor

apologies, it should be filePath, not filepath.  I didn't test it, just cobbled it together from the python interpreter and some old code I had to check for broken data links.

I added the print statements so you knew what was going on with the program.  They are also a great way to see what values are actually being used in python and make sure they are doing what you want.  Thats how I figured out your elm.name value earlier wasn't the right one for your code to work.

Not sure about line 13, whitespace can be annoying to work with, especially when cutting/pasting code, like I said I cobbled it together quickly. 

0 Kudos
JaredPilbeam1
Occasional Contributor

Ok, thanks. I fixed 'mxd.filePath'. The script ran with no errors and printed out "Current map being checked is..." for all the documents in the workspace, but with no results. 

Above in your test script you used \\Jackson\gis_dept\Data\Images\County-logo-1x1Small.JPG as your elem.name path and it worked. Does this mean it doesn't have to be a working file path? Of course, since that's a path to our directory here you can't access it. But, at the same time the Jackson network was discontinued a few days ago (hence the broken links). Or is the script recognizing that file path as the element name?

This is the message from Windows when I search for it:

So, I'm wondering if that's causing a problem in my script? 

Besides that I set the workspace to a working folder. And I used the whole path, as you did, for the elem.name:

import arcpy
import os
from arcpy import env

# change to directory with your map documents
env.workspace = r'R:\AtlasMaps\ATLAS_MAPS_17\New folder'

# recursively walking workspace
for root, dirs, files in os.walk(env.workspace):
     for f in files:
          if f.endswith(".mxd"):
               mxd = arcpy.mapping.MapDocument(os.path.join(root , f))
               print "Current map being checked is " + mxd.filePath
               for elem in arcpy.mapping.ListLayoutElements(mxd, "PICTURE_ELEMENT", "*logo"):
                   if elem.name == r"\\Jackson\gis_dept\Data\Images\County-logo-1x1Small.JPG":
                       elem.sourceImage = r"P:\Images\County-logo.JPG"

del elem
del mxd‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
IanMurray
Frequent Contributor

The filepath doesn't have to exist, it just had to be the value of the element name in the map drawing.  If you looked at the image I posted earlier using the mxd you posted, I used print statements to determine what the actual name of the picture element was in the map drawing.

Also I noticed another bug that might be causing the issue, line 13 needs an asterisk on the other side of logo, so it should be:

for elem in arcpy.mapping.ListLayoutElements(mxd, "PICTURE_ELEMENT", "*logo*"):

If it doesn't have a wildcard on both sides, it might not be pulling any picture elements at all, so it has no elements to even check(Since *logo would only find names ending with logo, instead of *logo* which looks to find names that contain the character string logo).  If that doesn't work, could you add a few print statements(even if it does work it helps show what is going on).  After line 13, could you add:

print "current element name is " + elem.name; print "current element source is " + elem.sourceImage

(same indent as line 14 currently). 

Then after the current line 15 add:

print "new element source is " + elem.sourceImage

(same indent as  line 15 currently)

0 Kudos
JaredPilbeam1
Occasional Contributor

Alright, great. At some point during testing I did try the asterisk on both sides of logo. 

I added the print statements and the asterisk. It ran with no errors, but no results to the map document again. I double checked and both instances of the JPG in the maps of the workspace are exactly: \\Jackson\gis_dept\Data\Images\County-logo-1x1Small.JPG. 

import arcpy
import os
from arcpy import env

# change to directory with your map documents
env.workspace = r'R:\AtlasMaps\ATLAS_MAPS_17\New folder'

# recursively walking workspace
for root, dirs, files, in os.walk(env.workspace):
    for f in files:
        if f.endswith(".mxd"):
            mxd = arcpy.mapping.MapDocument(os.path.join(root, f))
            print "current map being checked is " + mxd.filePath
            for elem in arcpy.mapping.ListLayoutElements(mxd, "PICTURE_ELEMENT", "*logo*"):
                print "current element name is " + elem.sourceImage
                if elem.name == r"\\Jackson\gis_dept\Data\Images\County-logo-1x1Small.JPG": #\\Jackson\gis_dept\Data\Images\County-logo-1x1Small.JPG
                    elem.sourceImage = r"P:\Images\County-logo-1x1Small.JPG"
                    print "new element source is " + elem.sourceImage

del elem
del mxd

I'm going to try with some different maps that have the have the same broken link to Jackson.

0 Kudos
IanMurray
Frequent Contributor

Okay, so I'm a bit of an idiot, I didn't bother to have the script actually save the map after the changes were made.  According to the the print statements the script is changing the source of your image, which is what we wanted, but the map document is not being saved with the changes.  Since I originally did this in ArcMap python window, I didn't need to save the map, I just refreshed the view to fix the changes.

Just add mxd.save() to line 18 with the same indent as line 11 and that should save the changes......

0 Kudos
JaredPilbeam1
Occasional Contributor

Brilliant! Works like a charm. Thanks for the help man.