I have created a script tool for ArcMap 10.3.1 that will move a legend around a data frame so as not to obscure a specified map layer when using data driven pages. However, I'm encountering an interesting problem when attempting to align the legend to the right edge of the data frame.

Here is the code I use to align the legend (the legend is anchored at the bottom left):

inset.elementPositionX = (DataFrame.elementPositionX +
   DataFrame.elementWidth) - inset.elementWidth

Now, this script works perfectly until I have the "Only show classes that are visible in the current map extent" box checked in the legend's properties. Having this checked causes the legend to resize as the program iterates through each page (depending on what is visible on the map), and when it does so it either leaves a gap or overruns the data frame edge.

The interesting thing is that while the "Only show classes that are visible in the current map extent" is checked on in the legend's properties, if I iterate through each page manually, running this little align snippet in the python window after changing pages, it works perfectly.

So, my question is: why does this not work while it's running in a script tool, but does when running manually? It's almost as if ArcMap isn't able to keep up with itself (if that even makes sense...)

Note* this tool was built to run on an open MXD, not outside of one.

To test try this:

Open an MXD, add two layers - one an index layer for DDP, and a second layer that is symbolized by categories. Make sure that not all categories show up in every DDP. Then, insert a legend, go to it's properties, check the "Only show classes that are visible in the current map extent" box. Then as you click through your DDPs, your legend should (unless all element descriptions are the same width) change shape.

Next, add this to the python window:

mxd = arcpy.mapping.MapDocument("Current")

SecondaryObject = "Legend"
inset = arcpy.mapping.ListLayoutElements(mxd, "", SecondaryObject)[0]

PrimeObject =  "Main View"
DataFrame = arcpy.mapping.ListDataFrames(mxd, PrimeObject)[0]

inset.elementPositionX = (DataFrame.elementPositionX + DataFrame.elementWidth) - inset.elementWidth

Next, iterate through your DDP to observe the snippet in action, working.

In the full version of the DDP with movable legend script, the align takes place after the DDP is switched to each new page. First the legend is moved back to it's starting position, and if the "intersect" function returns > 0 from there, it moves on to a new position:

for pageNum in range(1, mxd.dataDrivenPages.pageCount + 1):
    #setup naming and path for output maps
    path = mxd.filePath
    bn = os.path.basename(path)[:-4]
    mxd.dataDrivenPages.currentPageID = pageNum   

    insetDefaultX = inset.elementPositionX
    insetDefaultY = inset.elementPositionY

    #check defualt position for intersect
    intersect = checkIntersect(inset)

    if intersect == 0: #if it doesn't intersect, print the map
        arcpy.mapping.ExportToEPS(mxd, exportFolder + "\\" + bn + "_"+ str(pageNum) + ".eps", "Page_Layout",640,480,300,"BETTER","RGB",3,"ADAPTIVE","RASTERIZE_BITMAP",True,False)

    else: #intersect != 0: #move inset to SE corner
        inset.elementPositionX = (DataFrame.elementPositionX + DataFrame.elementWidth) - inset.elementWidth
        inset.elementPositionY = DataFrame.elementPositionY
  • I've re-opened it but what I would do to try and attract potential answerers is to describe the steps they could use to open a Blank Map, add one layer, insert a legend, enable DDP, etc. Then expand the code into a snippet that works up to where you are stuck i.e. Create the map document object from "current", and probably a few other lines to get to the one line of code that you are currently showing us. For me to try and answer your question I would need to do those steps, and because we are all time poor volunteers the more you can line up for us the better. – PolyGeo Nov 18 '15 at 23:54
  • This might be a silly question, but is it possible that the re-aligning of the legend in your script tool is being done before the dataframe and legend speak to each other to decide what layers are going to be shown in the legend? Full points for creating a tool to work around the issue though - I'd love to see something implemented by default in the software to handle this issue! – Adam Nov 19 '15 at 0:01
  • @Adam I think that is a possibility, and is what I was getting at when I said it's as if ArcMap isn't able to keep up with itself, but I'm not sure how to test it to find out (actually, haven't even tried to think of a way to test it). – CSB Nov 19 '15 at 14:57
  • Ahah. I must have read the question before the edit included that detail, but commented after! without having read the code, can you make your script refresh the active view before it works it's magic on repositioning the legend? – Adam Nov 19 '15 at 22:59
  • 1
    @Adam i posted that edit after (because of) your comment. i will try that out and get back to you. it may take a while - thanks for the idea! – CSB Nov 19 '15 at 23:35

Using refresh active view didn't fix my problem. Via esri help: "RefreshActiveView is only needed if you want to see the active view of the current map document updated. arcpy.mapping export, save, and printing functions will generate the expected updated results without use of RefreshActiveView."

Instead, the issue was with the location in my code where I first identified the legend and assigned its size and position to variables. I was doing this outside of my data driven pages check-intersect/export for loop, so it was always using the dimensions of the legend in its original state - i.e. its dimensions wherever it was when I started the code - not its dimensions after switching to a new page.

Once I moved this section down into my check-intersect/export for loop, the problem was fixed.

  • intersect = checkIntersect(inset) - Hi CSB, very interested in your code here, though everything makes sense till this checkIntersect bit, what is this referencing if you don't mind me asking. – Slevy Sep 12 '17 at 7:57
  • 1
    checkIntersect() is a function that converts the legend (in layout view) to a polygon shapefile (in data view), then checks if it intersects the layer we don't want to obscure. – CSB Sep 12 '17 at 16:27
  • Thanks CSB, assuming this is something you've developed in house? – Slevy Sep 13 '17 at 0:47
  • 1
    It's not completely my own. I'll post the code in your question here: gis.stackexchange.com/questions/254903/… – CSB Sep 19 '17 at 16:12

Insert arcpy.RefreshActiveView() in your script before testing for legend overlap.

  • i did try adding refresh active view, but it didn't help. moving my inset = into the for loop fixed it – CSB Nov 25 '15 at 16:52
  • So you've resolved the problem? Better add it as an answer :D – Adam Nov 25 '15 at 22:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.