Digital Geography

23. January 2015

Playing Around With ArcPY…

I was playing around with ArcPY and tried to recreate a version of the qgis2leaf plugin just for ArcGIS just to get to know that ArcGIS doesn’t seem to have native support for GeoJSON. So I’ll not show you how to build a leaflet map from within ArcGIS but I would like to show you some basic tasks in ArcPY by exporting a point shapefile to a CSV.

What Will We Do?

We will use a point shapefile and export it as a CSV file using ArcPY. So in the end you will have a new toolbox with a custom tool just for you.

What Will I Learn?

You will learn to:
  • iterate over features of a layer
  • file operations in ArcPY
  • create “interfaces” for your sript

What Do I need?

“Unfortunately” you’ll need a running version of ArcGIS version 10.x. A text editor with syntax highlighting like sublime 3 would be a plus.

1) Iterate Over Features

The iteration over features works with a so-called searchCursor:
The SearchCursor function establishes a read-only cursor on a feature class or table. The SearchCursor can be used to iterate through row objects and extract field values. The search can optionally be limited by a where clause or by field, and optionally sorted.
And according to the example in the help file it’s really easy, assuming our shapefile has the name “point_shape” and we are interested in the attribute name:
import arcpy
cursor = arcpy.SearchCursor("point_shape")
for row in cursor:
	print(row.getValue("name"))
This is quite easy and works like a charm. The geometry for a simple point shapefile is probably stored in the field called shape and we can also extract coordinates by row:
import arcpy
cursor = arcpy.SearchCursor("point_shape")
for row in cursor:
	print row.getValue("Shape").getPart().X, row.getValue("Shape").getPart().Y
This will print X and Y coordinates for every feature. As point features can be multipart as well we needed to call the getPart() function to get the part we are interested in for our singlepart points. If you’re not sure whether your shape information is stored in “Shape” as above you can ask the feature for the name of the shape information using the describe function:
desc = arcpy.Describe("point_shape")
desc.ShapeFieldName
By now we can iterate over features and print the coordinates and one attribute. As we don’t know the attribute names in the shapefiles by default we need to get a list of field names. We can do this right away using listFields:
fields = arcpy.ListFields("point_shape")
for field in fields:
	print field.name

2) File Writing

As we want to store all the information in a single file we need some other module to write a file and determine OS paths and so on:
import os
userhome = os.path.expanduser('~')
desktop = os.path.join(userhome,'Desktop')
For the file writing itself we will open a new file, write lines into it, save it and close it:
with open(desktop + os.sep + 'data.txt', 'w') as file:
	text = """This is my line to write to the file"""
	file.write(text)
	file.close()
I am using the triple quotes to be able to use single quotes in the string which I want to write to the file.

3) Bringing It Together

All we need to do now is to iterate over the features and write each feature information to a line. The basic code would look like this:
import arcpy
import os
cursor = arcpy.SearchCursor("point_shape")
fields = arcpy.ListFields("point_shape")
userhome = os.path.expanduser('~')
desktop = os.path.join(userhome,'Desktop')
text = ''
with open(desktop + os.sep + 'data.txt', 'w') as file:
	for field in fields: 
		text += field.name + """;"""
	text += """X;Y
"""
	for row in cursor:
		for field in fields:
			text += str(row.getValue(field.name)) + """;"""
		text += str(row.getValue("Shape").getPart().X) + """;""" + str(row.getValue("Shape").getPart().Y) + """
"""
	file.write(text)
	file.close()
The strange quotation marks mimic the line breaks we need to put into our file. Furthermore we need to change anything we get to a string so we use th str() function in line 15.

4) Making It Generic

At the moment we have one fixed attribute which is the name of the shapefile. As we want to call the tool independently from the shapefiles name we need to get this generic. We will combine this with the creation of the toolbox itself. But first: ArcPY has an easy way of generalisation when creating a toolbox we will use the first (or in Python language the “zeroest” element of inputs of the toolbox:
inputshape= arcpy.GetParameterAsText(0)
But where do we get the Parameter as text from? Open up the toolbox, Right click inside the toolbox window and select Add Toolbox,New Toolbox in the popup and give it a nice name:

Add New Toolbox

Now we need to add the script to the toolbox by right clicking the new toolbox and selecting Add Script:

Add Script Dialog

Select your saved python script from above in the next dialog:

Selected Script

Now comes the crucial part: We need to define the input parameter which is a point feature class in our case. So in the upper box select Feature Layer as Data Type and give it a name for the screen:

Input Dialog Definition

As we are just interested in Single Part Point feature classes we select Feature Class as Filter and deselect everything except Point In the end the tool looks like this and should only list point feature classes of your current project:

Export to CSV Tool

What we need to do in the end? Use the input in our script. So right-click on the script in the toolbox and edit it. Simply add the following line after our import XX lines:
inputshape= arcpy.GetParameterAsText(0)
This stores the name of the feature class in the variable inputshape. Now just replace these lines:
cursor = arcpy.SearchCursor("point_shape")
fields = arcpy.ListFields("point_shape")
with these two lines:
cursor = arcpy.SearchCursor(inputshape)
fields = arcpy.ListFields(inputshape)
That’s it! You can download the python script here.

What Next?

Try yourself to make the output filename generic/selectable.

Thank you for reading. Your comments will be valuable.

ps: There is a script/toolbox from @calvinmetcalf on github called esri2open which adds GeoJSON/CSV export to ArcGIS.
  • Richard Law

    ArcGIS Online seems to have recently started supporting GeoJSON: http://blogs.esri.com/esri/arcgis/2014/12/16/arcgis-online-geojson/

  • Doofus90210

    Hi, if I may add a few remarks to your code:

    Your SearchCursor object isn’t deleted, so the script does not
    necessarily remove the lock from the feature class you’re processing. To
    do this, add

    del cursor

    at the end, when you’re finished.

    Even better would be to use a with statement for the SearchCursor as
    well, as this will ensure that the feature class is unlocked even if the
    script throws an error. (credits to /u/sarcasmful[1] *)

    Your writer now writes the ‘value’ of the shapefield as well to the
    output file. This results in a column holding values like
    . Not
    very useful. To evade this, build in a check to only write values from
    fields for which

    field.name != shapeFieldName

    Or you could make a list of the field names using list comprehension:

    list_fields_names = [f.name for f in arcpy.ListFields(fc) if f.name != shape_field_name]

    Python has a built-in csv module, which lets you write any values to a
    comma separated text file, regardless of the presence of single or
    double quotes. Check it out here[2] .
    I find this more robust than playing around with triple-quotes and
    adding comma’s to every line. The only tricky thing I found about this
    is that the csv.writer object by default uses ‘rn’ as a line
    terminator. To evade this set lineterminator=’n’ when generating the
    writer object. Also, you don’t necessarily need to convert your values
    to string before writing them to the file.

    When you get X and Y values from the feature part, you call the
    getValue and getPart methods 2 times in one line. I would assign the
    part object to a temporary variable part and call up its attributes two
    times, as in

    part = row.getValue(shapeFieldName).getPart()

    and from that use part.X and part.Y to write to your file.

    Check out my version at http://txt.do/600g.

    I have tried this in arcpy from arcgis desktop 10.0. with statement
    doesn’t work with the arcpy.SearchCursor as it doesn’t have and exit attribute. According to /u/sarcasmful[4] it should work with the newer arcpy.da.SearchCursor.

    • Thanks for pasting your reddit comment!

  • Gato0

    Thanks for this!