I am new to #arcpy and I'm making my first script tool. Can I use an UpdateCursor with a SHAPE@ token and disjoint() instead of SelectLayerByLocation_management() followed by CalculateField_management()? Consider the code below, in which inputcampus is a polygon and in which lineoutput is a line feature class. I want to identify lines that cross inputcampus and for which (startcampus==0 and endcampus==0). Then I could categorize a trip as 'offcampus' if it started and ended off campus without ever entering campus or 'cross' if it starts off campus, goes on campus, and then ends off campus.
with arcpy.da.UpdateCursor(lineoutput, cursorfields) as cursor:
for row in cursor:
# the order of these variables is determined by the cursorfields list
# start_time= row[0]
# completed_time= row[1]
# trip_minutes= row[2]
# distance_meters= row[3]
# startcampus= row[4]
# endcampus= row[5]
# triptype= row[6]
# metersperminute= row[7]
# milesperhour= row[8]
# SHAPE@ (geometry) row[9]
tripminutes= round(((parser.parse(row[1]) - parser.parse(row[0])).total_seconds() / 60), 2)
row[2]= tripminutes
if tripminutes < 1: row[6]= 'zerotime'
elif row[3]==0: row[6]= 'zerodistance'
elif row[4]==1 and row[5]==1: row[6]= 'oncampus'
elif row[4]==1 and row[5]==0: row[6]= 'outgoing'
elif row[4]==0 and row[5]==1: row[6]= 'incoming'
elif row[4]==0 and row[5]==0: row[6]= 'offcampus'
# Because I don't know the proper syntax for the following, I did it outside of the cursor in lines 93-97
# This is sure to be less efficient so I hope to do it in the cursor instead
# row[9] uses a SHAPE@ token
# elif row[4]==0 and row[5]==0:
# if row[9].disjoint(inputcampus):
# row[6]= 'offcampus'
# else:
# row[6]= 'cross'
row[7]= round(( row[3] / tripminutes), 2) # meters per minute, rounded to 2 decimal places
row[8]= round(((row[3] / tripminutes) / 26.8224), 2) # (meters per minute) -> (miles per hour)
cursor.updateRow(row)
# I'd rather do the following in the cursor above, so the script is more efficient
arcpy.SelectLayerByAttribute_management('pylinelyr', "NEW_SELECTION", "startcampus = 0 AND endcampus = 0")
arcpy.SelectLayerByLocation_management( 'pylinelyr', "INTERSECT", inputcampus, selection_type="SUBSET_SELECTION")
arcpy.CalculateField_management('pylinelyr', 'triptype', 'cross')
arcpy.SelectLayerByAttribute_management('pylinelyr', 'CLEAR_SELECTION')I hope this makes sense. For context (and if you'd like to help me optimize my first script tool), here's the full code as it currently is written:
# This script was made to map and process bicycle trip data (WGS 1984) from CSV files with expected structure (see script parameters)
# Joshua Bailey, University of Central Florida, 3/5/2019
from dateutil import parser
import sys, arcpy
# Store parameters as variables to make code more manageable and readable
# Be sure ArcGIS tool dialog is configured in the correct order:
triptable= sys.argv[1] # input table/CSV containing trip data
inputcampus= sys.argv[2] # input campus polygon
startoutput= sys.argv[3] # start points output
endoutput= sys.argv[4] # end points output
lineoutput= sys.argv[5] # line output
tripid= sys.argv[6] # default= trip_id from triptable
distancemeters= sys.argv[7] # default= distance_meters from triptable
starttime= sys.argv[8] # default= start_time from triptable
completedtime= sys.argv[9] # default= completed_time from triptable
startlong= sys.argv[10] # default= start_longitude from triptable
startlat= sys.argv[11] # default= start_latitude from triptable
endlong= sys.argv[12] # default= end_longitude from triptable
endlat= sys.argv[13] # default= end_latitude from triptable
# Make feature classes from table/CSV
arcpy.MakeXYEventLayer_management(triptable, startlong, startlat, 'startXYlayer')
arcpy.MakeXYEventLayer_management(triptable, endlong, endlat, 'endXYlayer')
arcpy.CopyFeatures_management('startXYlayer', startoutput) # I think the event layers need to be exported so they have OIDs
arcpy.CopyFeatures_management('endXYlayer', endoutput)
arcpy.MakeFeatureLayer_management(startoutput, 'pystartlyr') # I think these layers have to be initialized for SelectLayerByLocation_management()
arcpy.MakeFeatureLayer_management(endoutput, 'pyendlyr')
arcpy.MakeFeatureLayer_management(lineoutput, 'pylinelyr')
arcpy.XYToLine_management(triptable, lineoutput, startlong, startlat, endlong, endlat, id_field=tripid)
# Add fields
arcpy.AddField_management(lineoutput, 'trip_minutes', 'FLOAT')
arcpy.AddField_management(lineoutput, 'metersperminute', 'FLOAT')
arcpy.AddField_management(lineoutput, 'milesperhour', 'FLOAT')
arcpy.AddField_management('pystartlyr', 'startcampus', 'SHORT')
arcpy.AddField_management('pyendlyr', 'endcampus', 'SHORT')
arcpy.AddField_management(lineoutput, 'triptype', 'TEXT')
# Mark start points within polygon: 1=within polygon; 0=not within polygon
arcpy.SelectLayerByLocation_management('pystartlyr', 'WITHIN', inputcampus)
arcpy.CalculateField_management('pystartlyr', 'startcampus', '1')
arcpy.SelectLayerByAttribute_management('pystartlyr', 'SWITCH_SELECTION')
arcpy.CalculateField_management('pystartlyr', 'startcampus', '0')
arcpy.SelectLayerByAttribute_management('pystartlyr', 'CLEAR_SELECTION')
arcpy.SelectLayerByLocation_management('pyendlyr', 'WITHIN', inputcampus)
arcpy.CalculateField_management('pyendlyr', 'endcampus', '1')
arcpy.SelectLayerByAttribute_management('pyendlyr', 'SWITCH_SELECTION')
arcpy.CalculateField_management('pyendlyr', 'endcampus', '0')
arcpy.SelectLayerByAttribute_management('pyendlyr', 'CLEAR_SELECTION')
arcpy.JoinField_management(lineoutput, tripid, startoutput, tripid, [starttime, completedtime, distancemeters, 'startcampus'])
arcpy.JoinField_management(lineoutput, tripid, endoutput, tripid, 'endcampus')
cursorfields= [starttime, completedtime, 'trip_minutes', distancemeters, 'startcampus', 'endcampus', 'triptype', 'metersperminute', 'milesperhour', 'SHAPE@']
with arcpy.da.UpdateCursor(lineoutput, cursorfields) as cursor:
for row in cursor:
# the order of these variables is determined by the cursorfields list
# start_time= row[0]
# completed_time= row[1]
# trip_minutes= row[2]
# distance_meters= row[3]
# startcampus= row[4]
# endcampus= row[5]
# triptype= row[6]
# metersperminute= row[7]
# milesperhour= row[8]
# SHAPE@ (geometry) row[9]
tripminutes= round(((parser.parse(row[1]) - parser.parse(row[0])).total_seconds() / 60), 2)
row[2]= tripminutes
if tripminutes < 1: row[6]= 'zerotime'
elif row[3]==0: row[6]= 'zerodistance'
elif row[4]==1 and row[5]==1: row[6]= 'oncampus'
elif row[4]==1 and row[5]==0: row[6]= 'outgoing'
elif row[4]==0 and row[5]==1: row[6]= 'incoming'
elif row[4]==0 and row[5]==0: row[6]= 'offcampus'
# Because I don't know the proper syntax for the following, I did it outside of the cursor in lines 93-97
# This is sure to be less efficient so I hope to do it in the cursor instead
# row[9] uses a SHAPE@ token
# elif row[4]==0 and row[5]==0: row[6]= 'offcampus'
# if row[9].disjoint(inputcampus):
# row[6]= 'offcampus'
# else:
# row[6]= 'cross'
row[7]= round(( row[3] / tripminutes), 2) # meters per minute, rounded to 2 decimal places
row[8]= round(((row[3] / tripminutes) / 26.8224), 2) # (meters per minute) -> (miles per hour)
cursor.updateRow(row)
# I'd rather do the following in the cursor above, so the script is more efficient
arcpy.SelectLayerByAttribute_management('pylinelyr', "NEW_SELECTION", "startcampus = 0 AND endcampus = 0")
arcpy.SelectLayerByLocation_management( 'pylinelyr', "INTERSECT", inputcampus, selection_type="SUBSET_SELECTION")
arcpy.CalculateField_management('pylinelyr', 'triptype', 'cross')
arcpy.SelectLayerByAttribute_management('pylinelyr', 'CLEAR_SELECTION')
# Create new field holding special ArcGIS data type (to be used in time-enabled maps)
arcpy.ConvertTimeField_management(lineoutput, starttime, 'yyyy-MM-ddTHH:mm:ss.000+00:00', 'start_DATE', 'DATE')
arcpy.ConvertTimeField_management(lineoutput, completedtime, 'yyyy-MM-ddTHH:mm:ss.000+00:00', 'end_DATE', 'DATE')
# The join fields are redundant in the following so I made a list variable
# Each JoinField_management() will append one field to the list
pointsjoin= ['trip_minutes', 'metersperminute', 'milesperhour', 'triptype', 'start_DATE', 'end_DATE']
arcpy.JoinField_management(startoutput, tripid, lineoutput, tripid, pointsjoin+['endcampus'])
arcpy.JoinField_management(endoutput, tripid, lineoutput, tripid, pointsjoin+['startcampus'])
Related: https://www.reddit.com/r/gis/comments/aw53a0/update_cursor_vs_calculate_field_speed/ https://community.esri.com/blogs/richard_fairhurst/2014/11/08/turbo-charging-data-manipulation-with-python-cursors-and-dictionaries