I'm unable to find an example of an ArcGIS Python Toolbox similar to the one I'm trying to get to work. The standalone python script works beautifully. Turning it into a Python Toolbox so I can use it in model builder / GP tool is where I'm having problems.
I'd like to be able to connect a point feature layer and a polyline feature layer to this tool as inputs, do some calculations, store the results in fields in the input point feature layer, and have the results available as an output feature layer. EDIT: My python script doesn't seem to have any output, per se. My thinking now is that I need the python script to create a feature class with all the fields, and then calculate them in the output (rather than the input).
The first input parameter, in_features should be a point feature set. The second input parameter is a single polyline, which in my case is a street centerline. The tool calculates station, offset and orientation values and places the results in their corresponding fields in the output feature layer. This means I can specify the output layer as "derived", right?
ArcCatalog reports no syntax errors. I get the message "This tool has no parameters" when trying to run the tool. The goal is to use this tool as a GP tool in ArcGIS Server. I'd like to be able to drop it into model builder and hook up the inputs and output.
Of course all this could be avoided if ESRI made the Near tool available to ArcGIS Standard license holders. Anyway, here's my CalculateStationOffset.pyt file:
Maybe I'm going about this all wrong. Perhaps I should be creating an output feature class in my Python code and calculating fields there. If I want the results to show up via the Web AppBuilder Geoprocessing widget, anyway. That's a whole other issue.
import arcpy
class Toolbox(object):
def __init__(self):
self.label = "Station Offset toolbox"
self.alias = "StationOffset"
# List of tool classes associated with this toolbox
self.tools = [CalculateStationOffset]
class CalculateStationOffset(object):
def __init__(self):
self.label = "Calculate Station Offset"
self.description = "Calculate Station Offset"
def getParameterInfo(self):
#Define parameter definitions
# Input Features parameter
in_features = arcpy.Parameter(
displayName="Input Features",
name="in_features",
datatype="GPFeatureLayer",
parameterType="Required",
direction="Input")
in_features.filter.list = ["Point"]
# Alignment parameter
in_alignment = arcpy.Parameter(
displayName="Alignment",
name="in_alignment",
datatype="GPFeatureLayer",
parameterType="Required",
direction="Input")
in_alignment.filter.list = ["Polyline"]
# Derived Output Features parameter
out_features = arcpy.Parameter(
displayName="Output Features",
name="out_features",
datatype="GPFeatureLayer",
parameterType="Derived",
direction="Output")
out_features.parameterDependencies = [in_features.name]
out_features.schema.clone = True
parameters = [in_features, in_alignment, out_features]
return parameters
def isLicensed(self):
return True
def updateParameters(self, parameters):
if parameters[0].altered:
parameters[1].value = arcpy.ValidateFieldName(parameters[1].value, parameters[0].value)
return
def updateMessages(self, parameters):
return
def execute(self, parameters, messages):
fc_pnt = parameters[0].valueAsText
fc_line = parameters[1].valueAsText
# Fields
fields = ['SHAPE@', 'MEAS', 'Distance', 'NEAR_X', 'NEAR_Y', 'Orient', 'POINT_X', 'POINT_Y', 'NEAR_ANGLE']
# Get line geometry - assumes only one feature in feature class
polyline = arcpy.da.SearchCursor(fc_line, "SHAPE@").next()[0]
# Loop over the point feature class
with arcpy.da.UpdateCursor(fc_pnt, fields) as cursor:
for row in cursor:
dist=polyline.queryPointAndDistance(row[0], False)
row[1] = dist[1]
row[2] = dist[2]
row[3] = dist[0].centroid.X
row[4] = dist[0].centroid.Y
if dist[3] == 0:
row[5]="Left"
else:
row[5]="Right"
row[6] = row[0].centroid.X
row[7] = row[0].centroid.Y
print(dist)
cursor.updateRow(row)
Answer
So the answer to my original question "Is it possible to have two input feature layers and a derived output feature layer in an ArcGIS Python Toolbox tool?" is of course yes.
Finally got the Python Toolbox to work. Was a combination of parts of my code not being indented correctly and working out the correct input and output tools in model builder to expose the parameters to the geoprocessing widget in Web AppBuilder. Next step is to figure out how to get it to do the calculation in TN State Plane NAD83 rather than Web Mercator (auxiliary sphere). Thanks everyone for your help.
import arcpy
class Toolbox(object):
def __init__(self):
self.label = "Station Offset toolbox"
self.alias = "StationOffset"
# List of tool classes associated with this toolbox
self.tools = [CalculateStationOffset]
class CalculateStationOffset(object):
def __init__(self):
self.label = "Calculate Station Offset"
self.description = "Calculate Station Offset"
def getParameterInfo(self):
#Define parameter definitions
# Input Features parameter
in_features = arcpy.Parameter(
displayName="Input Features",
name="in_features",
datatype="GPFeatureLayer",
parameterType="Required",
direction="Input")
in_features.filter.list = ["Point"]
# Alignment parameter
in_alignment = arcpy.Parameter(
displayName="Alignment",
name="in_alignment",
datatype="GPFeatureLayer",
parameterType="Required",
direction="Input")
in_alignment.filter.list = ["Polyline"]
# Derived Output Features parameter
out_features = arcpy.Parameter(
displayName="Output Features",
name="out_features",
datatype="GPFeatureLayer",
parameterType="Derived",
direction="Output")
out_features.parameterDependencies = [in_features.name]
out_features.schema.clone = True
parameters = [in_features, in_alignment, out_features]
return parameters
def isLicensed(self):
return True
def updateParameters(self, parameters):
if parameters[0].altered:
parameters[1].value = arcpy.ValidateFieldName(parameters[1].value, parameters[0].value)
return
def updateMessages(self, parameters):
return
def execute(self, parameters, messages):
fc_pnt = parameters[0].valueAsText
fc_line = parameters[1].valueAsText
# Fields
fields = ['SHAPE@', 'MEAS', 'Distance', 'NEAR_X', 'NEAR_Y', 'Orient', 'POINT_X', 'POINT_Y', 'NEAR_ANGLE']
# Get line geometry - assumes only one feature in feature class
polyline = arcpy.da.SearchCursor(fc_line, "SHAPE@").next()[0]
# Loop over the point feature class
with arcpy.da.UpdateCursor(fc_pnt, fields) as cursor:
for row in cursor:
dist=polyline.queryPointAndDistance(row[0], False)
row[1] = dist[1]
row[2] = dist[2]
row[3] = dist[0].centroid.X
row[4] = dist[0].centroid.Y
if dist[3] == 0:
row[5]="Left"
else:
row[5]="Right"
row[6] = row[0].centroid.X
row[7] = row[0].centroid.Y
print(dist)
cursor.updateRow(row)
No comments:
Post a Comment