Data Driven Pages Index Code

1650
3
01-20-2011 09:43 AM
ChrisMathers
Occasional Contributor III
I dont like the code for the index that is up on the resource center. So I wrote my own. This uses a page layout in arcmap with 4 columns that are 1.5 inchs wide. The font is Courier New at 9 pt so that allows for 21 characters per line and 69 lines per 10 inch column on an 8.5x11 page. I merged my roads with Unsplit Line before running this. I am thinking about generalizing this so that it can go on the resource center with little changing needed to adapt it to someone elses project. Unlike the other one out there, this one uses only standard library and arcpy functions and methods. This exports a slew of PDF documents which I dont use arcpy to merge. I tried it a few times and was unhappy with the result. It kept putting pages out of order.

import arcpy, textwrap, os, re
from operator import itemgetter
mxd=arcpy.mapping.MapDocument('CURRENT')
path = os.path.dirname(mxd.filePath)
arcpy.AddMessage('Creating Roads Feature Layer...')
arcpy.MakeFeatureLayer_management(path+ r"/Roads_UnsplitLine.shp",'Roads_UnsplitLine', '"ST_NAME"<>\'\'')
arcpy.AddMessage('Creating Grid FeatureLayer...')
arcpy.MakeFeatureLayer_management(path+ r'/Grid.shp','Grid')
 
arcpy.AddMessage('Making Road List...')
alphaList,numList=[],[]
for feature in arcpy.UpdateCursor('Roads_UnsplitLine'):
    arcpy.SelectLayerByLocation_management('Grid','INTERSECT',feature.SHAPE)
    tileList,roadName=[],[]
    for tile in arcpy.SearchCursor('Grid'):
        tileList.append('%s %s-%s'%(tile.Page,tile.COL,tile.ROW))
    if feature.DIR in ['E','W','S','N','NW']: DIR=feature.DIR
    else: DIR=' '
    if feature.ST_NAME[0].isdigit():
        match=re.search(r'(\d*)(\D*)', feature.ST_NAME)
        numList.append([DIR,int(match.group(1)),match.group(2),feature.ST_TYPE,', '.join(tileList)])
    else:
        alphaList.append([DIR,feature.ST_NAME,feature.ST_TYPE,', '.join(tileList)])
 
arcpy.AddMessage('Merging Strings...')
numList_merged=['%s %s%s %s: %s'%(road[0],road[1],road[2],road[3],road[4]) for road in sorted(numList,key=itemgetter(1,3,0))]
alphaList_merged=['%s %s %s: %s'%(road[0],road[1],road[2],road[3]) for road in sorted(alphaList,key=itemgetter(1,2,0))]
roadList=[road for road in alphaList_merged]
for road in numList_merged:
    roadList.append(road)
 
arcpy.AddMessage('Setting Up Word Wrapping...')
roadList_wrapped=[]
wrapper=textwrap.TextWrapper(width=21, subsequent_indent='  ', expand_tabs=False)
for line in roadList:
    line=str(line)
    for part in wrapper.wrap(line.lstrip()):
        roadList_wrapped.append(part)
 
roadList_split=['\n'.join(roadList_wrapped[pos:pos+69]) for pos in xrange(0, len(roadList_wrapped), 69)]
 
arcpy.AddMessage('Exporting PDF...')
One=arcpy.mapping.ListLayoutElements(mxd, 'TEXT_ELEMENT', 'One')[0]
Two=arcpy.mapping.ListLayoutElements(mxd, 'TEXT_ELEMENT', 'Two')[0]
Three=arcpy.mapping.ListLayoutElements(mxd, 'TEXT_ELEMENT', 'Three')[0]
Four=arcpy.mapping.ListLayoutElements(mxd, 'TEXT_ELEMENT', 'Four')[0]
 
count=1
for textbox in [roadList_split[pos:pos+4] for pos in xrange(0, len(roadList_split), 4)]:
    if len(textbox)==4:
        oneText,twoText,threeText,fourText=textbox[0],textbox[1],textbox[2],textbox[3]
    elif len(textbox)==3:
        oneText,twoText,threeText,fourText=textbox[0],textbox[1],textbox[2],' '
    elif len(textbox)==2:
        oneText,twoText,threeText,fourText=textbox[0],textbox[1],' ',' '
    elif len(textbox)==1:
        oneText,twoText,threeText,fourText=textbox[0],' ',' ',' '
    One.text,One.elementPositionX,One.elementPositionY=oneText,.6875,10.5
    Two.text,Two.elementPositionX,Two.elementPositionY=twoText,2.5625,10.5
    Three.text,Three.elementPositionX,Three.elementPositionY=threeText,4.4375,10.5
    Four.text,Four.elementPositionX,Four.elementPositionY=fourText,6.3125,10.5
    arcpy.mapping.ExportToPDF(mxd, path+ r'/IndexPage%s.pdf'%count)
    count+=1
 
One.text,One.elementPositionX,One.elementPositionY='BOX ONE',.6875,10.5
Two.text,Two.elementPositionX,Two.elementPositionY='BOX TWO',2.5625,10.5
Three.text,Three.elementPositionX,Three.elementPositionY='BOX THREE',4.4375,10.5
Four.text,Four.elementPositionX,Four.elementPositionY='BOX FOUR',6.3125,10.5
 

I tried to use list comprehensions as much as possible to cut down on the number of lines. If anyone has any questions about this script I would be happy to answer them.
Tags (2)
0 Kudos
3 Replies
ChrisMathers
Occasional Contributor III
I updated this a bit the other day. Just a few minor changes and wanted to post some read me text with it. I hope I can get time soon to make this generalized for the resource center.

import arcpy, textwrap, os, re
from operator import itemgetter
mxd=arcpy.mapping.MapDocument(r'CURRENT')
path = os.path.dirname(mxd.filePath)
arcpy.AddMessage('Creating Roads Feature Layer...')
arcpy.MakeFeatureLayer_management(arcpy.GetParameterAsText(0),'Roads')
arcpy.AddMessage('Creating Grid FeatureLayer...')
arcpy.MakeFeatureLayer_management(arcpy.GetParameterAsText(1),'Grid')
arcpy.AddMessage('Making Road List...')
alphaList,numList=[],[]
for feature in arcpy.SearchCursor('Roads'):
    arcpy.SelectLayerByLocation_management('Grid','INTERSECT',feature.SHAPE)
    tileList,roadName=[],[]
    GridCursor=arcpy.SearchCursor('Grid')
    for tile in GridCursor:
        if arcpy.GetParameter(2)==True:
            tileList.append('16R FU %s%s'%(tile.Eastings,tile.Northings))
        else:
            tileList.append('%s %s-%s'%(tile.Page,tile.COL,tile.ROW))
    if feature.ST_NAME[0].isdigit():
        match=re.search(r'(\d*)(\D*)', feature.ST_NAME)
        numList.append([feature.DIR,int(match.group(1)),match.group(2),feature.ST_TYPE,', '.join(tileList)])
    else:
        alphaList.append([feature.DIR,feature.ST_NAME,feature.ST_TYPE,', '.join(tileList)])
    del GridCursor
arcpy.AddMessage('Merging Strings...')
currentLetter='A'
alphaList_merged=['----------A---------']
for road in sorted(alphaList,key=itemgetter(1,2,0)):
    if road[1][0]!=currentLetter:
        currentLetter=road[1][0].upper()
        alphaList_merged.append('----------%s----------'%road[1][0].upper())
        alphaList_merged.append(('%s %s %s: %s'%(road[0],road[1],road[2],road[3])).lstrip())
    else:
        alphaList_merged.append(('%s %s %s: %s'%(road[0],road[1],road[2],road[3])).lstrip())
        
numList_merged=[('%s %s%s %s: %s'%(road[0],road[1],road[2],road[3],road[4])).lstrip() for road in sorted(numList,key=itemgetter(1,3,0))]
roadList=[road for road in alphaList_merged]
roadList.append('----------#----------')
for road in numList_merged:
    roadList.append(road)
arcpy.AddMessage('Setting Up Word Wrapping...')
roadList_wrapped=[]
wrapper=textwrap.TextWrapper(width=21, subsequent_indent='  ', expand_tabs=False)
for line in roadList:
    line=str(line)
    for part in wrapper.wrap(line):
        roadList_wrapped.append(part)
roadList_split=['\n'.join(roadList_wrapped[pos:pos+69]) for pos in xrange(0, len(roadList_wrapped), 69)]
arcpy.AddMessage('Exporting PDF...')
One=arcpy.mapping.ListLayoutElements(mxd, 'TEXT_ELEMENT', 'One')[0]
Two=arcpy.mapping.ListLayoutElements(mxd, 'TEXT_ELEMENT', 'Two')[0]
Three=arcpy.mapping.ListLayoutElements(mxd, 'TEXT_ELEMENT', 'Three')[0]
Four=arcpy.mapping.ListLayoutElements(mxd, 'TEXT_ELEMENT', 'Four')[0]

count=1
for textbox in [roadList_split[pos:pos+4] for pos in xrange(0, len(roadList_split), 4)]:
    if len(textbox)==4:
        oneText,twoText,threeText,fourText=textbox[0],textbox[1],textbox[2],textbox[3]
    elif len(textbox)==3:
        oneText,twoText,threeText,fourText=textbox[0],textbox[1],textbox[2],' '
    elif len(textbox)==2:
        oneText,twoText,threeText,fourText=textbox[0],textbox[1],' ',' '
    elif len(textbox)==1:
        oneText,twoText,threeText,fourText=textbox[0],' ',' ',' '
    One.text,One.elementPositionX,One.elementPositionY=oneText,.6875,10.5
    Two.text,Two.elementPositionX,Two.elementPositionY=twoText,2.5625,10.5
    Three.text,Three.elementPositionX,Three.elementPositionY=threeText,4.4375,10.5
    Four.text,Four.elementPositionX,Four.elementPositionY=fourText,6.3125,10.5
    arcpy.mapping.ExportToPDF(mxd, path+ r'/IndexPage%s.pdf'%count)
    count+=1
    
pdfDoc=arcpy.mapping.PDFDocumentCreate(path+r'/Index.pdf')
for num in xrange(1,len([roadList_split[pos:pos+4] for pos in xrange(0, len(roadList_split), 4)])+1):
    pdfDoc.appendPages(path+r'/IndexPage%s.pdf'%num)
pdfDoc.saveAndClose()
One.text,One.elementPositionX,One.elementPositionY='BOX ONE',.6875,10.5
Two.text,Two.elementPositionX,Two.elementPositionY='BOX TWO',2.5625,10.5
Three.text,Three.elementPositionX,Three.elementPositionY='BOX THREE',4.4375,10.5
Four.text,Four.elementPositionX,Four.elementPositionY='BOX FOUR',6.3125,10.5
arcpy.RefreshActiveView()


Some changes may need to be made to the section begining at line 17 and ending at
line 29. This section takes each road feature and selects the grid tiles which
intersect it and then writes both the road information and the intesection
information to a list for sorting and export later. By making the road data into a
nested list sorting the list into alphabetical order is made easier.
Inline comments should help for editing the code to fit your projects. Unfortunatly
due to differences in schemas and formatting between various organizations road and
grid feature classes this part cannot be generalized easily.
If your road data is split into short segments for addressing purposes it may be
a good idea to dissolve your roads into single features with the Unslpit Line or
Dissolve tool. This will cut down the number of pages in the index because the
same road will not appear multiple times.
The output for each road will be in the format 'Direction Name Type: Intersections'
'E 23RD CT: 118 B-1, 118 A-1, 117D-1, 118 C-1'
This is wrapped to fit a 21 character line:
E 23RD CT: 118 B-1,
  118 A-1, 117D-1,
  118 C-1
A sample page is included in PDF format.
Parameters:
0: Road data; Feature Layer
1: Grid data; Feature Layer

The code is currently set up for a schema like this:
Road: DIR,ST_NAME,ST_TYPE
Grid: COL,ROW
In the road data that my county uses numbered streets are named 1ST, 2ND, 3RD, etc.
This means that numbered streets are in the table as strings. Lines 25-27 takes
any road that has a name begining with a numeral and splits it into an integer and
a string i.e.E 1ST Ave becomes ['E',1,'ST','AVE']. This allows the numbered streets to
be sorted properly instead of normal string sorting.
0 Kudos
MariaJosefson
New Contributor II
Hi! I am trying out your code, and so far I've been able to modify it to my needs, but I am stuck on line:
One=arcpy.mapping.ListLayoutElements(mxd, 'TEXT_ELEMENT', 'One') [0]

I keep getting "IndexError: list index out of range". When I remove "[0]", the code gets through that line OK, but naturally gets stuck a short while later, when "oneText" is referenced. This at least suggests to me that it's not a problem with mxd directly? What should the mxd look like before the script starts, is it just empty? I am running this from outside ArcMap, so I am using a pre-saved mxd instead of Current. Could that be the problem? Thanks a lot for your help!
0 Kudos
MariaJosefson
New Contributor II
Update - I found that removing 'One' etc inside parentheses makes the code work (whu, I am not sure!). I am still having issues with formatting of the resulting page (only getting one column per page), and still rather confused about what the "empty" mxd should contain. Also, my understanding was that the street list would come out alphabetically, and it looks to be not - did I break something in the code, or was that not part of the program? Thanks!
0 Kudos