Creating serial graphs with arcpy

6345
8
04-03-2013 12:21 PM
StefanSchneider
New Contributor
Hello,
I need to make export reports with graphs. One map and one graph for each selected feature in a loop. When I add the graph to the layout and set the graph so that it only shows the values of the selected features it doesn't update when a new feature is selected with arcpy. Also setting the graph to show all values and making a query that only the selected feature is shown, doesn't work. Still the graph doesn't update. Embedding the graph to the layout of the map would be my favourite.
My second unsuccessful try was to select features and export the resulting graph with SaveGraph_management, but here I experienced the same problem.
When I select features manuelly/write a definition query manually the graph updates.
I would be really happy if you could help me, I've been trying to solve this problem for quite a few hours.
Thanks,
Pedol
Tags (2)
0 Kudos
8 Replies
CarlSunderman
Occasional Contributor
How are you creating the graph? i have successfully done this with arcpy and matplotlib for data driven pages.
0 Kudos
MichelleConvey
New Contributor II

Hi Carl,

I am trying to do exactly this but am struggling slightly.  Would you mind sharing the code with me at all please?

Many thanks in advance

0 Kudos
CarlSunderman
Occasional Contributor

Well, are you struggling slightly(almost there, just something in your code is not right) or are you lost (nothing works like it should)   Perhaps i can help you if you have something that just isn't quite working, if you are just lost,or not sure how to go about this.

My script uses numpy, matplotlib, and scipy, so you would need to install those modules in python (not sure if numpy is installed with arcpy, but i think it is.)

What my script does:

1. So you have a point feature class with a field that matches the DDP sheet number that it falls in

2. It then selects those features, and creates a graph, for that page generalizing for missing data to get rid of the jagged edges, as a png in the output folder

3. Then it inserts the graph onto the page and exports, and moves on to the next.

So at the end, in the output folder there are the pdf's with the graph inserted on them and all of the png's in case you want them for something else.

I really only use this as a function now to create the graphs, I have other functions in my tools that inserts graphs, graphics, pictures, labels, etc on the map. I tried to comment it to make it easier to understand, but let me know if you have any questions.

import arcpy,os,glob
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import spline

mxd = arcpy.mapping.MapDocument(r"<MXD file with Data Driven Pages>")
outputFolder = "C:/<ouput location>/"
# there should be a picture element on your page at the location where you want the graph inserted
pictureElementName = "<Picture Element Name>"
eleFieldFeature = "<Elevation Point Feature Class>"
eleField = "<Elevation Field>"
# distField can be whatever field you want for the X axis
distField = "<Distance Field>"

df = arcpy.mapping.ListDataFrames(mxd, "Layers")[0]
pict = arcpy.mapping.ListLayoutElements(mxd, "PICTURE_ELEMENT", pictureElementName)[0]
lyr = arcpy.mapping.ListLayers(mxd, eleFieldFeature, df)[0]


for pageNum in range(1, mxd.dataDrivenPages.pageCount + 1):
    mxd.dataDrivenPages.currentPageID = pageNum
    # change this SeqId to match the field name that you have in the DDP featureclass
    # this is redundant, but it works all the same and I didn't feel like changing it. :)
    pageName = mxd.dataDrivenPages.pageRow.getValue("SeqId")
    # change this SeqId to match the field name that you have in the Elevation featureclass
    arcpy.SelectLayerByAttribute_management(lyr, "NEW_SELECTION", ' "SeqId" = '+ repr(pageName))
    # create array of selected
    arr = arcpy.da.FeatureClassToNumPyArray(lyr, (eleField, distField))
    # set min and max for the graph from the array 
    mineleField = arr[eleField].min()
    maxeleField = arr[eleField].max()
    minStation = arr[distField].min()
    maxStation = arr[distField].max()
    # matplotlib stuff
    x = []
    y = []

    elevPNG = outputFolder+"\elev"+repr(pageNum)+".png"
    fig = plt.figure(figsize=(32, 2.8))
    
    table = lyr
    fields = [distField, eleField]
    with arcpy.da.SearchCursor(table, fields) as rows:
        for row in rows:
            x.append(row[0])
            y.append(row[1])


    # the next 7 lines generalize, smooth the line in case of missing data
    x_sm = np.array(x)
    y_sm = np.array(y)
    x_smooth = np.linspace(x_sm.min(), x_sm.max(),200)
    y_smooth = spline(x,y,x_smooth)
    # subtract/add 10 to the min max of the graph so elevation fits, can increase or decrease as you need
    plt.ylim((mineleField-10,maxeleField+10))
    plt.xlim((minStation,maxStation))
    plt.plot(x_smooth,y_smooth, color='red', linewidth=3)


    ## the next 3 is your X label, Y Label, and Graph Title
    plt.xlabel('Distance')
    plt.ylabel('Elevation')
    plt.title('Landscape Profile')
    plt.grid(True)
    fig.savefig(elevPNG,  bbox_inches='tight', dpi=(100))
    pict.sourceImage = elevPNG
    # end of matplotlib stuff 


    print "Exporting page {0} of {1}".format(str(mxd.dataDrivenPages.currentPageID), str(mxd.dataDrivenPages.pageCount))
    arcpy.mapping.ExportToPDF(mxd, outputFolder + str(pageName) + "_W_GRAPH.pdf", resolution=150, image_quality="NORMAL")
    plt.clf()
    plt.close()
    del fig
del mxd
MichelleConvey
New Contributor II

Hi Carl!  This helped immensely and I've now changed the chart to a bar chart which is what I needed for my output!  Thanks so much for your prompt reply and such detailed comments - much appreciated!

0 Kudos
CarlosSousa_Ferreira
New Contributor III

Thanks for this, Carl.

This script was very helpful. I just adapted it for getting the elevation and distance data directly from a polyline FeatureClass with Z and M coordinates and it worked like a charm. Thanks again.

0 Kudos
StefanSchneider
New Contributor
How are you creating the graph? i have successfully done this with arcpy and matplotlib for data driven pages.


I have created graphs with ArcGis (something like View --> Graphs --> Create Graph)  also in the Python window in ArcGIS without matplotlib, similiar to the sample script here http://resources.arcgis.com/en/help/main/10.1/index.html#//018z0000005t000000
0 Kudos
StefanSchneider
New Contributor
This is the code I tried to export graphs with but I get the same graph for every selected feature:

import arcpy,os
path = os.getcwd() 
pageCount = 1
mxd = arcpy.mapping.MapDocument(path + r"\test_graph_exp.mxd")
df = arcpy.mapping.ListDataFrames(mxd)[0]
lyr = arcpy.mapping.ListLayers(mxd, "Counties_sel")[0]
list_of_attributes = [row.getValue('Name') for row in arcpy.SearchCursor(lyr.dataSource)]
 
for bez in list_of_attributes:
     #Make a graph for each line of the attribute table
     whereClause = "\"name\" = '" + bez + "'"
     lyr.definitionQuery = whereClause
     arcpy.SelectLayerByAttribute_management(lyr,"NEW_SELECTION", whereClause)
     arcpy.SaveGraph_management("test_graph", (path + r"\graph" + str(pageCount) + ".bmp") , "MAINTAIN_ASPECT_RATIO", "600", "375")
     arcpy.SelectLayerByAttribute_management(lyr, "CLEAR_SELECTION")               
     pageCount = pageCount + 1
del mxd
0 Kudos
StefanSchneider
New Contributor
In a new mxd the code works. The problem seems to have been with the mxd. Thank you very much for trying to help me.
0 Kudos