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
Solved! Go to Solution.
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.
copy and paste and format the relevant code so everyone can see it.
Please see my edited question.
It is either a format and paste problem. Did you open up the syntax highlighter and select python before you pasted the code in? because some looks properly formatted, the rest doesn't
Yes, I did. I manually indented the ones with 'if-else' statement.
Hello,
It's annoying how Geonet's syntax highlighter widget doesn't indent anything. So, when you try to drag one of the polygon layers into the first parameter of your tool, what specifically happens? It won't accept it as an input?
What happens if you delete or comment out line 37?
I don't use Python toolboxes myself, and I have to say after seeing this post I feel good about that choice! Side question - what efficiency to folks get out of using Python toolboxes over regular old custom toolboxes?
Good luck out there!
Micah
It only finds 'Street' as Input Feature and no other polygons. I don't understand why.
Line 37 ... ... name = 'field_name'?
about the formatting
/blogs/dan_patterson/2016/08/14/script-formatting
easiest way
Dan has a point about the code's formatting. The indentation level is important. Using the script you supplied, the indentation should look like this.
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
I would also suggest using some blank lines to help make the code more readable.
Thanks, how is this relate to question? Are the codes work normal on your side?