Calculating geometry using pyscripting: failed to read polygon input feature

3210
20
Jump to solution
10-03-2016 08:33 PM
GoldenJiang
New Contributor II

I'm following an ESRI online learning tutorial: Creating Python Toolboxes Using ArcGIS 10.1.

I copy the script and test on my data but the new tool only recognises 'Street' as an input feature. 

Can anyone tell me where is wrong with the codes below? 

import arcpy

class Toolbox(object):
 def __init__(self):
 """Define the toolbox (the name of the toolbox is the name of the
 .pyt file)."""
 self.label = "Calculate Geometry"
 self.alias = "geometry"
# List of tool classes associated with this toolbox
 self.tools = [CalculateGeometry]

class CalculateGeometry(object):
 def __init__(self):
 """Define the tool (tool name is the name of the class)."""
 self.label = "CalculateGeometry"
 self.description = ""
 self.canRunInBackground = True
def getParameterInfo(self):
# first parameter
 """Define parameter definitions"""
 in_features = arcpy.Parameter(
 displayName = 'Input Feature',
 name = 'in_features',
 datatype = 'Feature Layer',
 parameterType = 'Required',
 direction = 'Input')
 in_features.filter.list = ['Point','Polyline','Polygon']
# second parameter
 field = arcpy.Parameter(
 displayName = 'Field name',
 name = 'field_name',
 datatype = 'Field',
 parameterType = 'Required',
 direction = 'Input')
field.parameterDependencies = [in_features.name]
 # only show field belongs to the input feature
 in_features.filter.list = ['Short','Long','Double','Float','Text']
# third parameter
 geomProperty = arcpy.Parameter(
 displayName = 'Property',
 name = 'geomProperty',
 datatype = 'String',
 parameterType = 'Required',
 direction = 'Input')
# fourth parameter
 units = arcpy.Parameter(
 displayName = 'Units',
 name = 'units',
 datatype = 'String',
 parameterType = 'optional',
 direction = 'Input',
 enabled = False)
# fifth parameter
 out_features = arcpy.Parameter(
 displayName = 'Output Features',
 name = 'out_features',
 datatype = 'Feature Layer',
 parameterType = 'Derived',
 direction = 'Output')
field.parameterDependencies = [in_features.name]
 out_features.schema.clone = True
params = [in_features, field, geomProperty, units, out_features]
 return params

 def isLicensed(self):
 """Set whether tool is licensed to execute."""
 return True
def updateParameters(self, parameters):
 """Modify the values and properties of parameters before internal
 validation is performed. This method is called whenever a parameter
 has been changed."""
# Get Inputs
 in_features = parameters[0]
 geomProperty = parameters[2]
 units = parameters[3]
# Geometry Property Filter Lists
 pointPropertyList = ['X Coordinate of Point',
 'Y Coordinate of Point']
 linePropertyList = ['Length', 'X Coordinate of Line Start',
 'Y Coordinate of Line Start',
 'X Coordinate of Line End',
 'Y Coordinate of Line End']
 polygonPropertyList = ['Area','Perimeter',
 'X Coordinate of Centroid',
 'Y Coordinate of Centroid']
# Get shape type of input to determine
 # filter list for geometry property parameter
if in_features.value:
    desc = arcpy.Describe(in_features.valueAsText)
    if desc.shapeType == 'Point':
       geomProperty.filter.list = pointPropertyList
    elif desc.shapeType == 'Polyline':
       geomProperty.filter.list = linePropertyList
    elif desc.shapeType == 'Polygon':
       geomProperty.filter.list = polygonPropertyList
# Unit Filter Lists
 areaUnitList = ['Acres','Ares','Hectares','Square Centimeters','Square Inches','Square Feet','Square Kilometers','Square Meters','Square Miles','Square Millimeters','Square Yard','Square Decimeters']
 lengthUnitList = ['Centimeters','Feet','Inches','Kilometers','Meters','Miles','Millimeters','Nautical Miles','Yards','Decimal Degrees']
# Get geometry property input to determine filter list for unit parameter
 if geomProperty.value:
    if geomProperty.valueAsText == 'Area':
       units.enabled = True
       units.filter.list = areaUnitList
    elif geomProperty.valueAsText in ['Length','Perimeter']:
       units.enabled =True
       units.filter.list = lengthUnitList
    else:
       units.value = ''
       units.filter.list = []
       units.enabled = False
    return

 def updateMessages(self, parameters):
 """Modify the messages created by internal validation for each tool
 parameter. This method is called after internal validation."""
in_features = parameters[0]
 geomProperty= parameters[2]
 units = parameters[3]
# Check is input is Multipoint or Multipatch and if so set an error
 if in_features.value:
    desc = arcpy.Describe(in_features.valueAsText)
    if desc.shapeType in ['Multipoint','Multipatch']:
      in_features.setErrorMessage('{0} features are not supported.'.format(desc.shapeType))
# Check if certain geometry property value is set with no units and add error
 if geomProperty.valueAsText in ['Length','Perimeter','Area']:
    if not units.value:
       units.setErrorMessage('Units required for {0} property'.format(geomProperty.valueAsText))
 return

 def execute(self, parameters, messages):
 """The source code of the tool."""
 # Get Input
 in_features = parameters[0].valueAsText
 field = parameters[1].valueAsText
 geomProperty= parameters[2].valueAsText
 units = parameters[3].valueAsText
shapeFieldName = arcpy.Describe(in_features).shapeFieldName
# Create the expression
 exp = '!' + shapeFieldName
 if geomProperty == 'Area':
    exp += '.area@'+ units.replace('','')
 elif geomProperty in ['Length','Perimeter']:
    exp += '.length@'+ units.replace('','')
 else:
    propertyList = geomProperty.split('')
    coord = propertyList[0]
    if propertyList[-1] in ['Point','Start']:
       exp += '.firstPoint.' + coord
    elif propertyList[-1] == 'End':
       exp += '.lastPoint.'+ coord
    elif propertyList[-1] == 'Centroid':
       exp += '.centroid.'+ coord
exp+= '!'

messages.addMessage(
 '\nExpression used for field calculation: {0}\n'.format(exp))

#Calculate Field
 arcpy.CalculateField_management(in_features,field,exp,'PYTHON_9.3')
return‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
20 Replies
MicahBabinski
Occasional Contributor III

Hey Golden, it seems like quite a challenge to me! Is this tutorial part of a class? A couple thoughts:

Have you looked at regular custom script tools stored in a .tbx? I don't use Python Toolbox .pyts. I think it's a lot of extra lines of code and I generally get all I need from regular script tools where the parameter properties are all set up within the GUI.

Also, if posting a lengthy script for others to scrutinize, it's worth the time spent on formatting, commenting, spacing out so folks can understand it better and get you some more worthwhile feedback.

Best,

Micah

GoldenJiang
New Contributor II

Yes, this is what they use as an example for ESRI online learning tutorial. 

You are right and there is a lot of extra lines in define parameter and validation that may not be necessary. 

I generally use exiting tools for map automation but no idea how to develop my own tools. 

Anyway, I still couldn't find what it is wrong. I've thought it would be somewhere during validation. But I have compared the codes very carefully and it seems to be the same as the tutorial. 

  

0 Kudos
MicahBabinski
Occasional Contributor III

I've gotten stuck on Esri tutorials before. It's frustrating! One thought: are you using the right tutorial data? It looks like you are using data for an ArcGIS 10.0 tutorial:

However, Python toolboxes are only supported starting at version 10.1. I'm not sure if this is the issue but maybe verify you are using the correct tutorial data. Also, I am still not clear on the purpose of this line:

It looks like you are filtering a list of possible input features with different types of attribute columns? Seems a little odd, but again I am not very familiar with Python toolbox syntax and setup.

Here's a final thought for you, Golden: the Esri documentation for Defining parameter data types in a Python toolbox, it looks like the proper syntax for a feature layer input would actually be: 

datatype = 'GPFeatureLayer'

You might give it a try with that modified syntax.

Good on you for taking on Python toolboxes! Here's some more info on Comparing custom and Python toolboxes.

Micah

GoldenJiang
New Contributor II

Thanks, I don't have the example data they use for tutorial so I just use mine.

I tried the codes on another data but it still doesn't read polygon.  

I tried `datatype = 'GPFeatureLayer'` but it still doesn't work.  

0 Kudos
RandyBurton
MVP Alum

I reposted my code above as I also had some indentation errors.  Lines 19-27 limit the type of feature layers the tool will use - points, lines and polygons.  For some reason the filter on line 27 is limiting the feature type to lines.  If you comment the line out, it will then allow points and polygons.  

        # in_features.filter.list = ['Point','Polyline','Polygon']

But this may cause the code to fail later on.  In the training seminar video, POINT etc. is in all caps, but it doesn't seem to be a problem with capitalization alone.  This will take some further investigation.

Regarding Micah's question about line 37:

        in_features.filter.list = ['Short','Long','Double','Float','Text']

This line is limiting the choice display of the selected layer's fields to the specific types in the list.  Fields of type Date, Blob, Raster, Guid, etc. are not displayed.

0 Kudos
GoldenJiang
New Contributor II

Thanks Randy, it works when I commented out line 27 but I still don't understand why. 

Can you please explain a little more? 

0 Kudos
RandyBurton
MVP Alum

Golden, I don't know exactly why it is happening.  My first guess would be a bug in version 10.2.1, because I don't seem have the issue with 10.4.1.  (I have only tested with these versions of ArcMap.)  I have been able to get a listing of point, line and polygon layers with just 'Point' in the list on line 27.  Perhaps one of the items in the list is incorrect, but from the documentation all looks ok.  I am also wondering if line 37 is correctly filtering the fields as well, but it doesn't seem to be causing a serious problem.

In place of commenting out the line, it is also possible to use an empty list such as:

        in_features.filter.list = []

Since the filter class is built into the Python toolbox, I'm not quite sure how to debug it.  There is some documentation here and here.

0 Kudos
GoldenJiang
New Contributor II

I am also using ArcGIS 10.4. OK then this is weird ... ...  

I think commenting is the same as deleting it.  So this line is not required? 

in_feature.filter.list = ['Polygon', 'Polyline', 'Points']
0 Kudos
RandyBurton
MVP Alum

Commenting out the line is like deleting it.  The purpose of the line is to limit the layers to types that the tool is designed to work with.  But for some reason, the filter class in the Python toolbox code does not seem to be working properly, and I'm not sure how to debug it.  If you limit the input layers to these three types, the tool will probably work.

GoldenJiang
New Contributor II

Would that relate to the environment the input features are in?  For example, if the file is under a folder instead of a Geodatabase. 

0 Kudos