Calculating geometry using pyscripting: failed to read polygon input feature

1873
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
1 Solution

Accepted Solutions
RandyBurton
MVP Regular Contributor

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.

View solution in original post

0 Kudos
20 Replies
DanPatterson_Retired
MVP Esteemed Contributor

copy and paste and format the relevant code so everyone can see it.  

0 Kudos
GoldenJiang
New Contributor II

Please see my edited question.

0 Kudos
DanPatterson_Retired
MVP Esteemed Contributor

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

0 Kudos
GoldenJiang
New Contributor II

Yes, I did. I manually indented the ones with 'if-else' statement. 

0 Kudos
MicahBabinski
Regular Contributor

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

GoldenJiang
New Contributor II

It only finds 'Street' as Input Feature and no other polygons. I don't understand why. 

Line 37 ... ... name = 'field_name'? 

0 Kudos
DanPatterson_Retired
MVP Esteemed Contributor

about the formatting 

/blogs/dan_patterson/2016/08/14/script-formatting 

easiest way

  • copy the code from you python script
  • flip to a comment and bring up the syntax highligher (follow the ... (dots))
  • select Python from the syntax options.
  • paste (ctrl v or whatever)
  • save
RandyBurton
MVP Regular Contributor

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.

GoldenJiang
New Contributor II

Thanks, how is this relate to question? Are the codes work normal on your side?