Clipping layers while using Data Driven Pages

One of the hardest things about writing a blog is deciding on a title for the article… I’m still not convinced this is the right title to describe this article…. But anyway….

Apart from talking about Python and ArcPy I also teach anything and everything about ArcGIS for Desktop and on a recent course I was asked this question:

“I know that Data Driven Pages can be used to create a map based upon the extent of each feature in the index layer…. But how can I display just that feature from my Data Driven Pages index layer, and not any other features? And can I choose what layers are clipped by the outline of the index layer?

It has been ages since I wrote a blog post (apologies but work and life have gotten in the way in the latter part of 2016 / early 2017 ) and this question has stimulated me to finally write something – Action this Day if you like…

In my map document I have a basemap from ArcGIS Online and three feature layers, made up of building outlines, ward boundaries and the road network. My wards layer is my Data Driven Pages index layer and is currently displaying Grange Ward.

My map is shown below – apologies to any cartographers who might have stumbled across this blog entry. Please leave your carto-criticisms in the comments section where they will be dealt with in the traditional way by most complaints departments….

01_BASE_LAYOUT

So let’s answer the second question first – “How can I display just that feature from my Data Driven Pages index layer, and not any other features?”

This is achieved by working with the Data Frame Properties dialog box. So:

  • Click the Data Frame tab and notice the area called “Clip Options”.

03_ClipOptions

By default it is set to No Clipping.

  • Click on the drop-down and choose “Clip to current data driven page extent” and press the OK button.

04_ClipToDDP

When the changes have been applied you will then see that the current Data Driven Page feature is used to clip all of the layers (including raster layers) in your map. Notice that only the current index layer feature is displayed (Grange Ward) and it is used to clip all features and pixels within the active Data Frame.

05_ClippedAllLayers

Pressing the Next Page button on the Data Driven Pages toolbar will display the next Data Driven Page feature in the index layer and will also clip the data accordingly.

06_DDPToolbar

Now that the current feature in the index layer is clipping the data it is quite straightforward to control what layers are clipped and what ones are not clipped. The second question –  “And can I choose what layers are clipped by the outline of the index layer?” can now be answered.

Once again this is done from within the Data Frame Properties > Data Frame tab.

  • Locate the Clip Options area (the Clip to current data driven page extent option should still be chosen).

Notice the Exclude Layers… button.

07_ExcludeButton

  • Click the Exclude Layers… button.
  • Choose the layers which are no longer to be clipped. In the example below the basemap and the roads are chosen and will no longer be clipped to the current index layer feature.

08_ExcludeLayers

The result is that the OS Lines are clipped to the single Grange Ward index feature. The roads and the basemap are no longer clipped to the index feature.

09_ClippedOS_Lines

To highlight your particular clipping feature a border style can be applied:

10_Boarder

The map below shows the clipping feature with a “Single, Nautical Dash”:

11_SingleNauticalBorder

So, you probably knew this anyway: I didn’t and thought it was a nice feature I wanted to share.

I wonder if ArcGIS Pro 2.0 has this built into it yet? Something to look at as and when…

 

Advertisements

How do I specify optional arguments in a geoprocessing tool?

Here is another common question I get asked quite a bit within the “Introduction to geoprocessing scripts using Python” course:

When I am calling a geoprocessing tool from within arcpy how can I specify an optional argument?

So, when running a geoprocessing tool within your script you must supply all mandatory arguments, at the very least for the tool to run. You can also supply additional optional arguments if you need to. Traditionally you had to make sure that these optional arguments were supplied in a strict order as stipulated by the tool’s syntax.

Let’s have a look at the Make Feature Layer tool found in the Data Management Toolbox. This is a useful tool that allows you to create a feature layer which can then be used in further geoprocessing tools such as Select Layer By Location. It also has a few optional arguments which you can specify.

Looking at the help for the tool you can see that it is made up of mandatory arguments (arguments which must be specified at the very least for the tool to begin execution), and optional arguments, specified by curly brace brackets {}.

Syntax

MakeFeatureLayer_management (in_features, out_layer, {where_clause}, {workspace}, {field_info})

There are times when I would like to use the field_info optional parameter as I might wish to rename an attribute field found in my input layer to a new attribute field in my resulting feature layer. As you can see from looking at the tool’s syntax, the field_info parameter is the fifth argument in the tool’s parameter list and the third optional parameter.

So how do you make sure that the optional field_info parameter is passed into the tool at its correct position?

There are two main ways in achieving this:

1: Maintain order

The first way of specifying that I wish to use the field_info parameter is by creating placeholders for the previous unrequired optional arguments. The placeholders can be one of the following: an empty set of quotation marks (“”), a hash sign inside of a string (“#”) or by using the None type.

For example:


fldInfo = arcpy.FldInfo()
fldInfo.AddField("PC", "PostCode", "VISIBLE", "NONE")
arcpy.MakeFeatureLayer_management(inputFC,
                                  outputFL,
                                  "",
                                  "",
                                  fldInfo)

The disadvantages of this approach is that additional code is required to display these placeholders and in a tool which has many optional parameters (for example Topo to Raster in the Spatial Analyst toolbox) it is quite easy to lose one’s position within the argument list.

2: Use keyword arguments

A much neater way of overcoming the specifying of the placeholders is to only include the optional arguments you want by fully qualifying the optional argument with its parameter name, for example:

field_info = fldInfo

The syntax for the Make Feature Layer tool would now read:


arcpy.MakeFeatureLayer_management(inputFC,
                                  outputFL,
                                  field_info = fldInfo)

The advantages of this approach are that:

1: You are only including the optional arguments within the tool’s argument list

2: It is easier to see what arguments are included in the argument list

3: Your code is much more readable

Please note that this notation can only be used if you are an ArcGIS for Desktop 10.1 user, or above. If you are working with ArcGIS for Desktop 10.0 then you will have to use the notation in which you specify placeholders.

and there you have it!

** EDIT ** Many thanks to Jason Scheirer ( @jasonscheirer ) for pointing out a previous mistake in this post.

Working with distance (linear) units in a geoprocessing script

In this article I will answer another common question I am often asked in class:

“When, for example, I want to perform a buffer what linear units are available and where can I get a definitive list of them?

There are many geoprocessing tools which allow to specify some kind of distance unit as an input into the tool. These distance units are usually associated with the linear unit data type. Examples of these tools include Buffer, Generate Near Table, Grid Index Features, to name but a few. In fact the more you look the more you realise that this tool input is really quite common.

Using distance (linear) units in a geoprocessing tool is straight-forwards. Let’s use the trusty Buffer tool as an example:

arcpy.Buffer_analysis(“Roads”,
                      “RoadsBuff100”,
                      100)

In the above example we are buffering the Roads layer to a distance of 100. But is this 100 metres? 100 feet? Or 100 miles? It all depends upon the linear unit of the input dataset’s coordinate system. In the United Kingdom the coordinate system tends to be British National Grid whose unit of measurement is the metre so the Roads layer will be buffered to 100 metres.

A common question I get asked is:

“What about if I wanted to buffer the roads to 100 feet? How would I do that?”

In the Buffer tool dialog this pretty easy as I can select my units from a drop down list:

Unitsdropdown

How would I do this in my Python script?

It is relatively straight-forwards. All you have to do is make the buffer distance into a string and include the distance units of choice inside of the string:

arcpy.Buffer_analysis(“Roads”,
                      “RoadsBuff100”,
                      “100 feet”)

If your distance (linear) units are composed of more than one word, such as “Decimal Degrees” then the two words should be combined into one i.e. “DecimalDegrees

The follow-up question invariably is:

“How do I know what distance units are available to me?”

Well, you could look in the drop down list on the dialog for the tool concerned. Or you could look in the ArcGIS for Desktop Help and find the well-hidden help page which lists the linear units. The page is found at this link.

If you do not have an internet connection then you can find the relevant help document here:

Desktop >

Geoprocessing >

Tool errors and warnings >

Tool errors 1 – 10000 >

Tool errors and warnings: 801 – 900 >

000817 : Invalid linear unit type

In the next blog I will have a look another common question: “What is the best way to specify optional arguments for a geoprocessing tool?”

How can I check to see if an attribute field exists using ArcPy?

Something I get asked in virtually every “Introduction to geoprocessing scripts using Python” course is:

Using Python and ArcPy, how do I know if an attribute field exists?”.

This is an important question to ask if you are data-loading into an existing dataset using an Update Cursor or writing to a brand new dataset using an Insert Cursor because you may need to make sure that, at the very least, a particular attribute field exists or you may need to make a new one to support some data loading of attribute data.

In the ArcPy site package there is no “FieldExists()” function.…. so how do you determine whether an attribute field exists in a dataset?

Possibly the best way is to use a combination of the arcpy.ListFields() function, and count the length of the resulting list.

When you inspect the documentation for the arcpy.ListFields() function, you will see that it returns a Python list of field objects. You will also see that it has an optional wildcard argument. This allows you to find all those attribute fields beginning with or containing the specified search string.

To see if an attribute field exists you should add the full name of the field as the wildcard. If the resulting list has exactly one item inside it then you know the field is present within your table or feature class. If the list is empty then the wild card has not found the attribute field in the dataset which means the field does not exist.

Let’s see how this works in a code sample. I want to find out if the ST_NAME field exists in the Address feature layer as presented below:

FieldExists

If the field does not exist then create a new field using the Add Field geoprocessing tool. If it does exist then great! The script can continue to execute.

# Check to see if the ST_NAME attribute field exists
import arcpy
arcpy.env.workspace = r”C:\Student\PYTH\Database\corvallis.gdb”
fields = arcpy.ListFields(“Address”, “ST_NAME”)
if len(fields) != 1:
    arcpy.AddField_management(“Address”, “ST_NAME”, “TEXT”)

# Perform rest of script workflow once field is present…

And there you have it…!