11 Replies Latest reply on Sep 21, 2011 2:39 PM by npeihl

    Arcpy Dataframe

    djriff
      Hi everyone, i'm trying to make a script that does the following

      Pulls the coordinates from the active dataframe and inputs them into a polygon for a preselected feature class. In addition populates certain attribute fields from data saved in the MXD such as scale and creation date. Also there are some other particulars in the fields that I want calculate such as Producer, Notes, Product Type, Country Code. I've taken a look at Dataframe Extent under Arcpy and was wondering if it was possible to pull the current extent from the active dataframe. Looking at the example code I made a theory of using the "Current" command to pull the coordinates.

      df = arcpy.mapping.ListDataFrames(mxd)[0]
      newExtent = df.extent
      newExtent.XMin, newExtent.YMin = -180.0, -90.0
      newExtent.XMax, newExtent.YMax = 180.0, 90.0
      df.extent = newExtent
      


      That's the example code I found located at http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/DataFrame/00s300000003000000/  Any help and advice and help is greatly appreciated.
        • Re: Arcpy Dataframe
          mzcoyle
          Creating a polygon is fairly simple.

          mxd = arcpy.mapping.MapDocument("CURRENT")
          df = arcpy.mapping.ListDataFrames(mxd,"Layers")[0]
          
          extent = df.extent
          array = arcpy.Array()
          
          array.add(extent.lowerLeft)
          array.add(extent.lowerRight)
          array.add(extent.upperRight)
          array.add(extent.upperLeft)
          array.add(extent.lowerLeft)
           
          polygon = arcpy.Polygon(array)
          arcpy.CopyFeatures_management(polygon, r"C:\GIS\Default.gdb\polytest")
          


          After that adding fields and inputting some map document properties shouldn't be hard, but I don't have an example of it handy. See http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/MapDocument/00s30000000n000000/
          • Re: Arcpy Dataframe
            npeihl
            I have a mostly-completed project that makes polygons of the main dataframe of each MXD in a given folder and saves the MXD properties and layer information in a file geodatabase. You can take a look at my code at https://bitbucket.org/npeihl/mapindextoolv2/overview.

            I apologize for lack of documentation. I haven't put it into production yet.
            • Re: Arcpy Dataframe
              corea
              Hi,

              I've created a script to do what you need and it's attached. Using the same code structure you should be able to populate the extra fields that you want.

              If you have any issues please tell me as I would like to improve the script.

              best,
              • Re: Arcpy Dataframe
                djriff
                I actually was able to work through most of this. I was going to do the fields later as i'm taking this one step at a time. Right now the newest thing I am trying to implement is being able to select which dataframe from the mxd you want to use. The reason for this is that the amount of mxd's i'm looking at 500+ have different dataframe names. Now i'm using arcpy.GetParameter(0) as i'm running this through ArcGIS and not from python so that it is more user friendly to people who do not know python coding. basically rough gist of script so far is:

                import arcpy, os, sys
                
                mxdin = acrpy.GetParameter(0)
                
                
                mxd = arcpy.mapping.MapDocument(mxdin)
                df = arcpy.mapping.ListDataFrames(mxd,"Layers")[0]
                
                extent = df.extent
                array = arcpy.Array()
                
                array.add(extent.lowerLeft)
                array.add(extent.lowerRight)
                array.add(extent.upperRight)
                array.add(extent.upperLeft)
                array.add(extent.lowerLeft)
                 
                try:
                
                  polygon = arcpy.Polygon(array)
                  arcpy.CopyFeatures_management(polygon, r"C:\GIS\Default.gdb\polytest"
                
                except:
                  print "Failed to create polygon"
                


                I've thought of a couple ways to do it, I haven't tried anything yet though. Some of my ideas were to put the list of dataframes into an array and have a parameter that lets the user select from a drop down list which dataframe to use. Ideas?
                • Re: Arcpy Dataframe
                  djriff
                  Ok I now have a working script that does everything I wanted to do. Some things i've noticed while running this is hat calculate fields is particularly slow, is there a way to speed it up or another function I can use instead? I took a few things from corea's script, thank you btw it was a great help. I have thought about using the rows part but i'm unsure how to use that exactly. This whole thing has been a wonderful learning process for me and i'm happy I was able to get to where i'm at now, thank you everyone :)

                  Here is my script in its current form.

                  # Dataframe To Feature Class tool.
                  # Pulls the extent of the active dataframe in an mxd, and fills out fields based on input values
                  # from the user.
                  # Author:  LCpl Patrick Fischer
                  # Riff.Ibanezius@gmail.com
                  # 3D Topo Plt, 3D Intel BN, Camp Hansen Okinawa, Japan
                  
                  import arcpy, os, sys
                  
                  # Deletes feature class from previous time the script was ran.
                  arcpy.Delete_management(r"Z:\Training\Python\test_scripts\Extent_map.gdb\polygon")
                  
                  # Collect Parameters from User input
                  
                  mxdin = arcpy.GetParameterAsText(0)
                  
                  mxd = arcpy.mapping.MapDocument(mxdin)
                  
                  df = arcpy.mapping.ListDataFrames(mxd, "La*") [0] # select main dataframe not location diagram
                  
                  
                  ext = df.extent
                  array = arcpy.Array()
                  
                  array.add(ext.lowerLeft)
                  array.add(ext.lowerRight)
                  array.add(ext.upperRight)
                  array.add(ext.upperLeft)
                  array.add(ext.lowerLeft)
                  
                  polygon = arcpy.Polygon(array, df.spatialReference)
                  
                  arcpy.AddMessage("Extent has successfully been converted to a polygon")
                  
                  
                  #Checks spatial reference from the dataframe to see if it needs to be transformed into another projection.
                  
                  if df.spatialReference == "WGS 1984":
                      arcpy.Project_management(polygon, r"Z:\Training\Python\test_scripts\Extent_Map.gdb\polygon", "C:\Program Files\ArcGIS\Desktop10.0\Coordinate Systems\Geographic Coordinate Systems\World\WGS 1984.prj")
                  
                  else:
                      arcpy.CopyFeatures_management(polygon, r"Z:\Training\Python\test_scripts\Extent_Map.gdb\polygon")
                      print 'Already projected into WGS 1984' #change to message box
                  
                  arcpy.AddMessage("Successfuly projected into WGS 1984")
                  polypro = r"Z:\Training\Python\test_scripts\Extent_Map.gdb\polygon"
                  
                  #add fields to new feature class
                  
                  arcpy.AddField_management(polypro, "PRODUCT_ID", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                  arcpy.AddField_management(polypro, "PRODUCT_DATE", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                  arcpy.AddField_management(polypro, "SCALE", "FLOAT", "", "", 50, "", "NULLABLE", "REQUIRED")
                  arcpy.AddField_management(polypro, "EDITION", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                  arcpy.AddField_management(polypro, "CLASSIFICATION", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                  arcpy.AddField_management(polypro, "TYPE", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                  arcpy.AddField_management(polypro, "PRODUCER", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                  arcpy.AddField_management(polypro, "COUNTRY_CODE", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                  arcpy.AddField_management(polypro, "NOTES", "TEXT", "", "", 250, "", "NULLABLE", "REQUIRED")
                  arcpy.AddField_management(polypro, "PDF_PATH", "TEXT", "", "", 250, "", "NULLABLE", "REQUIRED")
                  
                  #Set parameters for user input
                  
                  PROID = arcpy.GetParameterAsText(1)
                  PRODA = arcpy.GetParameterAsText(2)
                  ED = arcpy.GetParameterAsText(3)
                  CLA = arcpy.GetParameterAsText(4)
                  TYP = arcpy.GetParameterAsText(5)
                  PROD = arcpy.GetParameterAsText(6)
                  CC = arcpy.GetParameterAsText(7)
                  NOTES = arcpy.GetParameterAsText(8)
                  PDF = arcpy.GetParameterAsText(9)
                  
                  arcpy.AddMessage("Calculating fields based on input values")
                  
                  #Calculate Fields based on user input
                  for el in arcpy.mapping.ListLayoutElements(mxd, "DATAFRAME_ELEMENT"):
                      SCALE = el.scale
                      arcpy.CalculateField_management(polypro, "PRODUCT_ID", PROID, "", "")
                      arcpy.CalculateField_management(polypro, "PRODUCT_DATE", PRODA, "", "")
                      arcpy.CalculateField_management(polypro, "SCALE", SCALE, "", "")
                      arcpy.CalculateField_management(polypro, "EDITION", ED, "", "")
                      arcpy.CalculateField_management(polypro, "CLASSIFICATION", CLA, "", "")
                      arcpy.CalculateField_management(polypro, "TYPE", TYP, "", "")
                      arcpy.CalculateField_management(polypro, "PRODUCER", PROD, "", "") 
                      arcpy.CalculateField_management(polypro, "COUNTRY_CODE", CC, "", "")
                      arcpy.CalculateField_management(polypro, "NOTES", NOTES, "", "")
                      arcpy.CalculateField_management(polypro, "PDF_PATH", PDF, "", "")
                  
                  arcpy.AddMessage("Succesfully calculated fields, appending to Topo Products feature class.")
                  
                  Feature = arcpy.GetParameterAsText(10)
                  
                  arcpy.Append_management(polypro, Feature ", "NO_TEST", "", "")
                  
                  


                  Also sometimes i'll get an error about list is out of range, on one mxd it was due to what I think was to many data frames (around 50), but it happened again on another with just 2 data frames. I've ran this successfully with mxds with up to 5 data frames with no issues. Is this an anomaly or is something seriously wrong?
                  • Re: Arcpy Dataframe
                    mzcoyle
                    I find update cursor to be faster than calculate field in most operations.

                    Example
                    rows = arcpy.UpdateCursor(layer)
                    for row in rows:
                        row.values = 1
                        rows.updateRow(row)
                    


                    For more detail see
                    http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//000v0000003m000000
                    • Re: Arcpy Dataframe
                      npeihl
                      Ok I now have a working script that does everything I wanted to do. Some things i've noticed while running this is hat calculate fields is particularly slow, is there a way to speed it up or another function I can use instead? I took a few things from corea's script, thank you btw it was a great help. I have thought about using the rows part but i'm unsure how to use that exactly. This whole thing has been a wonderful learning process for me and i'm happy I was able to get to where i'm at now, thank you everyone :)

                      Here is my script in its current form.

                      # Dataframe To Feature Class tool.
                      # Pulls the extent of the active dataframe in an mxd, and fills out fields based on input values
                      # from the user.
                      # Author:  LCpl Patrick Fischer
                      # Riff.Ibanezius@gmail.com
                      # 3D Topo Plt, 3D Intel BN, Camp Hansen Okinawa, Japan
                      
                      import arcpy, os, sys
                      
                      # Deletes feature class from previous time the script was ran.
                      arcpy.Delete_management(r"Z:\Training\Python\test_scripts\Extent_map.gdb\polygon")
                      
                      # Collect Parameters from User input
                      
                      mxdin = arcpy.GetParameterAsText(0)
                      
                      mxd = arcpy.mapping.MapDocument(mxdin)
                      
                      df = arcpy.mapping.ListDataFrames(mxd, "La*") [0] # select main dataframe not location diagram
                      
                      
                      ext = df.extent
                      array = arcpy.Array()
                      
                      array.add(ext.lowerLeft)
                      array.add(ext.lowerRight)
                      array.add(ext.upperRight)
                      array.add(ext.upperLeft)
                      array.add(ext.lowerLeft)
                      
                      polygon = arcpy.Polygon(array, df.spatialReference)
                      
                      arcpy.AddMessage("Extent has successfully been converted to a polygon")
                      
                      
                      #Checks spatial reference from the dataframe to see if it needs to be transformed into another projection.
                      
                      if df.spatialReference == "WGS 1984":
                          arcpy.Project_management(polygon, r"Z:\Training\Python\test_scripts\Extent_Map.gdb\polygon", "C:\Program Files\ArcGIS\Desktop10.0\Coordinate Systems\Geographic Coordinate Systems\World\WGS 1984.prj")
                      
                      else:
                          arcpy.CopyFeatures_management(polygon, r"Z:\Training\Python\test_scripts\Extent_Map.gdb\polygon")
                          print 'Already projected into WGS 1984' #change to message box
                      
                      arcpy.AddMessage("Successfuly projected into WGS 1984")
                      polypro = r"Z:\Training\Python\test_scripts\Extent_Map.gdb\polygon"
                      
                      #add fields to new feature class
                      
                      arcpy.AddField_management(polypro, "PRODUCT_ID", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                      arcpy.AddField_management(polypro, "PRODUCT_DATE", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                      arcpy.AddField_management(polypro, "SCALE", "FLOAT", "", "", 50, "", "NULLABLE", "REQUIRED")
                      arcpy.AddField_management(polypro, "EDITION", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                      arcpy.AddField_management(polypro, "CLASSIFICATION", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                      arcpy.AddField_management(polypro, "TYPE", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                      arcpy.AddField_management(polypro, "PRODUCER", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                      arcpy.AddField_management(polypro, "COUNTRY_CODE", "TEXT", "", "", 50, "", "NULLABLE", "REQUIRED")
                      arcpy.AddField_management(polypro, "NOTES", "TEXT", "", "", 250, "", "NULLABLE", "REQUIRED")
                      arcpy.AddField_management(polypro, "PDF_PATH", "TEXT", "", "", 250, "", "NULLABLE", "REQUIRED")
                      
                      #Set parameters for user input
                      
                      PROID = arcpy.GetParameterAsText(1)
                      PRODA = arcpy.GetParameterAsText(2)
                      ED = arcpy.GetParameterAsText(3)
                      CLA = arcpy.GetParameterAsText(4)
                      TYP = arcpy.GetParameterAsText(5)
                      PROD = arcpy.GetParameterAsText(6)
                      CC = arcpy.GetParameterAsText(7)
                      NOTES = arcpy.GetParameterAsText(8)
                      PDF = arcpy.GetParameterAsText(9)
                      
                      arcpy.AddMessage("Calculating fields based on input values")
                      
                      #Calculate Fields based on user input
                      for el in arcpy.mapping.ListLayoutElements(mxd, "DATAFRAME_ELEMENT"):
                          SCALE = el.scale
                          arcpy.CalculateField_management(polypro, "PRODUCT_ID", PROID, "", "")
                          arcpy.CalculateField_management(polypro, "PRODUCT_DATE", PRODA, "", "")
                          arcpy.CalculateField_management(polypro, "SCALE", SCALE, "", "")
                          arcpy.CalculateField_management(polypro, "EDITION", ED, "", "")
                          arcpy.CalculateField_management(polypro, "CLASSIFICATION", CLA, "", "")
                          arcpy.CalculateField_management(polypro, "TYPE", TYP, "", "")
                          arcpy.CalculateField_management(polypro, "PRODUCER", PROD, "", "") 
                          arcpy.CalculateField_management(polypro, "COUNTRY_CODE", CC, "", "")
                          arcpy.CalculateField_management(polypro, "NOTES", NOTES, "", "")
                          arcpy.CalculateField_management(polypro, "PDF_PATH", PDF, "", "")
                      
                      arcpy.AddMessage("Succesfully calculated fields, appending to Topo Products feature class.")
                      
                      Feature = arcpy.GetParameterAsText(10)
                      
                      arcpy.Append_management(polypro, Feature ", "NO_TEST", "", "")
                      
                      


                      Also sometimes i'll get an error about list is out of range, on one mxd it was due to what I think was to many data frames (around 50), but it happened again on another with just 2 data frames. I've ran this successfully with mxds with up to 5 data frames with no issues. Is this an anomaly or is something seriously wrong?


                      I agree with Matthew that you should use a cursor instead of Calculating Fields. An Insert Cursor should work nicely for you.

                      Also, the "list out of range" error may be due to no Dataframes in the MXD that start with "La" as you specify when you are creating the "df" variable. If ArcPy can't find any dataframes in the MXD that start with "La" then it creates an empty list. Calling the first index "[0]" on an empty list returns the out of range error.
                      • Re: Arcpy Dataframe
                        djriff
                        I agree with Matthew that you should use a cursor instead of Calculating Fields. An Insert Cursor should work nicely for you.

                        Also, the "list out of range" error may be due to no Dataframes in the MXD that start with "La" as you specify when you are creating the "df" variable. If ArcPy can't find any dataframes in the MXD that start with "La" then it creates an empty list. Calling the first index "[0]" on an empty list returns the out of range error.


                        So i used cursor and rows and found it much more to my advantage. Works quite nicely. Still want to know how to put the list of dataframes into a list you can select from in arcCatalog.
                        • Re: Arcpy Dataframe
                          mzcoyle
                          Still want to know how to put the list of dataframes into a list you can select from in arcCatalog.


                          Not sure I quite understand what you are looking for. You mean just a table of the data frame names?
                          • Re: Arcpy Dataframe
                            djriff
                            Not sure I quite understand what you are looking for. You mean just a table of the data frame names?


                            That's pretty much what i'm looking for. A table that you can select which data frame you want to take the extent from.
                            • Re: Arcpy Dataframe
                              npeihl
                              That's pretty much what i'm looking for. A table that you can select which data frame you want to take the extent from.


                              You could retrieve the dataframes, print them as a list of options for the user and then get the user's response via "raw_input". However, I don't know if this will work when the script is used in an ArcToolbox.

                              I envision it something like this:

                              # Get a list of dataframes in the mxd and assign the list to df
                              dataFrames = arcpy.ListDataframes(mxd)
                              
                              # Give each dataframe in the df list a number starting with 1 using enumerate and make it a dictionary
                              options = dict(enumerate(dataFrames, 1))
                              
                              # Show options and let the user select which dataframe
                              response = int(raw_input("Select the number of the dataframe you want: %s \n" % options.items()))
                              
                              # Assign df to the user's choice
                              df = options[response]
                              
                              


                              If you use that code you may want to build in some try:except clauses to send user-friendly error messages if bad info is entered.